@diagonal-hq/cli 0.2.0 → 0.3.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.
package/dist/cli.mjs CHANGED
@@ -17028,7 +17028,7 @@ ${lanes.join("\n")}
17028
17028
  initial
17029
17029
  );
17030
17030
  }
17031
- function appendCommentRange(pos, end, kind, hasTrailingNewLine, _state2, comments = []) {
17031
+ function appendCommentRange(pos, end, kind, hasTrailingNewLine, _state, comments = []) {
17032
17032
  comments.push({ kind, pos, end, hasTrailingNewLine });
17033
17033
  return comments;
17034
17034
  }
@@ -136786,7 +136786,7 @@ ${lanes.join("\n")}
136786
136786
  function onLeft(next, _workArea, parent2) {
136787
136787
  return maybeEmitExpression(next, parent2, "left");
136788
136788
  }
136789
- function onOperator(operatorToken, _state2, node) {
136789
+ function onOperator(operatorToken, _state, node) {
136790
136790
  const isCommaOperator = operatorToken.kind !== 28;
136791
136791
  const linesBeforeOperator = getLinesBetweenNodes(node, node.left, operatorToken);
136792
136792
  const linesAfterOperator = getLinesBetweenNodes(node, operatorToken, node.right);
@@ -141766,9 +141766,9 @@ ${lanes.join("\n")}
141766
141766
  /*ignoreCase*/
141767
141767
  false
141768
141768
  )) {
141769
- const basename3 = getBaseFileName(a.fileName);
141770
- if (basename3 === "lib.d.ts" || basename3 === "lib.es6.d.ts") return 0;
141771
- const name = removeSuffix(removePrefix(basename3, "lib."), ".d.ts");
141769
+ const basename4 = getBaseFileName(a.fileName);
141770
+ if (basename4 === "lib.d.ts" || basename4 === "lib.es6.d.ts") return 0;
141771
+ const name = removeSuffix(removePrefix(basename4, "lib."), ".d.ts");
141772
141772
  const index3 = libs.indexOf(name);
141773
141773
  if (index3 !== -1) return index3 + 1;
141774
141774
  }
@@ -205714,8 +205714,8 @@ ${options2.prefix}` : "\n" : options2.prefix
205714
205714
  }
205715
205715
  };
205716
205716
  for (const file2 of files) {
205717
- const basename3 = getBaseFileName(file2);
205718
- if (basename3 === "package.json" || basename3 === "bower.json") {
205717
+ const basename4 = getBaseFileName(file2);
205718
+ if (basename4 === "package.json" || basename4 === "bower.json") {
205719
205719
  createProjectWatcher(
205720
205720
  file2,
205721
205721
  "FileWatcher"
@@ -209399,8 +209399,8 @@ All files are: ${JSON.stringify(names2)}`,
209399
209399
  var _a;
209400
209400
  const fileOrDirectoryPath = removeIgnoredPath(this.toPath(fileOrDirectory));
209401
209401
  if (!fileOrDirectoryPath) return;
209402
- const basename3 = getBaseFileName(fileOrDirectoryPath);
209403
- if (((_a = result.affectedModuleSpecifierCacheProjects) == null ? void 0 : _a.size) && (basename3 === "package.json" || basename3 === "node_modules")) {
209402
+ const basename4 = getBaseFileName(fileOrDirectoryPath);
209403
+ if (((_a = result.affectedModuleSpecifierCacheProjects) == null ? void 0 : _a.size) && (basename4 === "package.json" || basename4 === "node_modules")) {
209404
209404
  result.affectedModuleSpecifierCacheProjects.forEach((project) => {
209405
209405
  var _a2;
209406
209406
  (_a2 = project.getModuleSpecifierCache()) == null ? void 0 : _a2.clear();
@@ -217123,8 +217123,8 @@ var require_base = __commonJS({
217123
217123
  function _interopRequireDefault(obj) {
217124
217124
  return obj && obj.__esModule ? obj : { default: obj };
217125
217125
  }
217126
- var _state2 = require_state();
217127
- var _state22 = _interopRequireDefault(_state2);
217126
+ var _state = require_state();
217127
+ var _state2 = _interopRequireDefault(_state);
217128
217128
  var _charcodes = require_charcodes();
217129
217129
  exports.isJSXEnabled;
217130
217130
  exports.isTypeScriptEnabled;
@@ -217168,7 +217168,7 @@ var require_base = __commonJS({
217168
217168
  exports.locationForIndex = locationForIndex;
217169
217169
  function initParser(inputCode, isJSXEnabledArg, isTypeScriptEnabledArg, isFlowEnabledArg) {
217170
217170
  exports.input = inputCode;
217171
- exports.state = new (0, _state22.default)();
217171
+ exports.state = new (0, _state2.default)();
217172
217172
  exports.nextContextId = 1;
217173
217173
  exports.isJSXEnabled = isJSXEnabledArg;
217174
217174
  exports.isTypeScriptEnabled = isTypeScriptEnabledArg;
@@ -233699,7 +233699,7 @@ var require_expression = __commonJS({
233699
233699
  var _typescript = require_typescript2();
233700
233700
  var _index3 = require_tokenizer();
233701
233701
  var _keywords = require_keywords();
233702
- var _state2 = require_state();
233702
+ var _state = require_state();
233703
233703
  var _types3 = require_types();
233704
233704
  var _charcodes = require_charcodes();
233705
233705
  var _identifier = require_identifier();
@@ -234352,14 +234352,14 @@ var require_expression = __commonJS({
234352
234352
  _statement.parseFunctionParams.call(void 0, allowModifiers, funcContextId);
234353
234353
  parseFunctionBodyAndFinish(functionStart, funcContextId);
234354
234354
  const endTokenIndex = _base.state.tokens.length;
234355
- _base.state.scopes.push(new (0, _state2.Scope)(startTokenIndex, endTokenIndex, true));
234355
+ _base.state.scopes.push(new (0, _state.Scope)(startTokenIndex, endTokenIndex, true));
234356
234356
  _base.state.scopeDepth--;
234357
234357
  }
234358
234358
  exports.parseMethod = parseMethod;
234359
234359
  function parseArrowExpression(startTokenIndex) {
234360
234360
  parseFunctionBody(true);
234361
234361
  const endTokenIndex = _base.state.tokens.length;
234362
- _base.state.scopes.push(new (0, _state2.Scope)(startTokenIndex, endTokenIndex, true));
234362
+ _base.state.scopes.push(new (0, _state.Scope)(startTokenIndex, endTokenIndex, true));
234363
234363
  _base.state.scopeDepth--;
234364
234364
  }
234365
234365
  exports.parseArrowExpression = parseArrowExpression;
@@ -235323,7 +235323,7 @@ var require_statement = __commonJS({
235323
235323
  var _typescript = require_typescript2();
235324
235324
  var _tokenizer = require_tokenizer();
235325
235325
  var _keywords = require_keywords();
235326
- var _state2 = require_state();
235326
+ var _state = require_state();
235327
235327
  var _types = require_types();
235328
235328
  var _charcodes = require_charcodes();
235329
235329
  var _base = require_base();
@@ -235332,7 +235332,7 @@ var require_statement = __commonJS({
235332
235332
  var _util = require_util();
235333
235333
  function parseTopLevel() {
235334
235334
  parseBlockBody(_types.TokenType.eof);
235335
- _base.state.scopes.push(new (0, _state2.Scope)(0, _base.state.tokens.length, true));
235335
+ _base.state.scopes.push(new (0, _state.Scope)(0, _base.state.tokens.length, true));
235336
235336
  if (_base.state.scopeDepth !== 0) {
235337
235337
  throw new Error(`Invalid scope depth at end of file: ${_base.state.scopeDepth}`);
235338
235338
  }
@@ -235543,7 +235543,7 @@ var require_statement = __commonJS({
235543
235543
  const startTokenIndex = _base.state.tokens.length;
235544
235544
  parseAmbiguousForStatement();
235545
235545
  const endTokenIndex = _base.state.tokens.length;
235546
- _base.state.scopes.push(new (0, _state2.Scope)(startTokenIndex, endTokenIndex, false));
235546
+ _base.state.scopes.push(new (0, _state.Scope)(startTokenIndex, endTokenIndex, false));
235547
235547
  _base.state.scopeDepth--;
235548
235548
  }
235549
235549
  function isUsingInLoop() {
@@ -235634,7 +235634,7 @@ var require_statement = __commonJS({
235634
235634
  }
235635
235635
  _tokenizer.next.call(void 0);
235636
235636
  const endTokenIndex = _base.state.tokens.length;
235637
- _base.state.scopes.push(new (0, _state2.Scope)(startTokenIndex, endTokenIndex, false));
235637
+ _base.state.scopes.push(new (0, _state.Scope)(startTokenIndex, endTokenIndex, false));
235638
235638
  _base.state.scopeDepth--;
235639
235639
  }
235640
235640
  function parseThrowStatement() {
@@ -235668,7 +235668,7 @@ var require_statement = __commonJS({
235668
235668
  parseBlock();
235669
235669
  if (catchBindingStartTokenIndex != null) {
235670
235670
  const endTokenIndex = _base.state.tokens.length;
235671
- _base.state.scopes.push(new (0, _state2.Scope)(catchBindingStartTokenIndex, endTokenIndex, false));
235671
+ _base.state.scopes.push(new (0, _state.Scope)(catchBindingStartTokenIndex, endTokenIndex, false));
235672
235672
  _base.state.scopeDepth--;
235673
235673
  }
235674
235674
  }
@@ -235714,7 +235714,7 @@ var require_statement = __commonJS({
235714
235714
  _base.state.tokens[_base.state.tokens.length - 1].contextId = contextId;
235715
235715
  }
235716
235716
  const endTokenIndex = _base.state.tokens.length;
235717
- _base.state.scopes.push(new (0, _state2.Scope)(startTokenIndex, endTokenIndex, isFunctionScope));
235717
+ _base.state.scopes.push(new (0, _state.Scope)(startTokenIndex, endTokenIndex, isFunctionScope));
235718
235718
  _base.state.scopeDepth--;
235719
235719
  }
235720
235720
  exports.parseBlock = parseBlock;
@@ -235787,10 +235787,10 @@ var require_statement = __commonJS({
235787
235787
  parseFunctionParams();
235788
235788
  _expression.parseFunctionBodyAndFinish.call(void 0, functionStart);
235789
235789
  const endTokenIndex = _base.state.tokens.length;
235790
- _base.state.scopes.push(new (0, _state2.Scope)(startTokenIndex, endTokenIndex, true));
235790
+ _base.state.scopes.push(new (0, _state.Scope)(startTokenIndex, endTokenIndex, true));
235791
235791
  _base.state.scopeDepth--;
235792
235792
  if (nameScopeStartTokenIndex !== null) {
235793
- _base.state.scopes.push(new (0, _state2.Scope)(nameScopeStartTokenIndex, endTokenIndex, true));
235793
+ _base.state.scopes.push(new (0, _state.Scope)(nameScopeStartTokenIndex, endTokenIndex, true));
235794
235794
  _base.state.scopeDepth--;
235795
235795
  }
235796
235796
  }
@@ -235839,7 +235839,7 @@ var require_statement = __commonJS({
235839
235839
  _base.state.tokens[_base.state.tokens.length - 1].contextId = contextId;
235840
235840
  if (nameScopeStartTokenIndex !== null) {
235841
235841
  const endTokenIndex = _base.state.tokens.length;
235842
- _base.state.scopes.push(new (0, _state2.Scope)(nameScopeStartTokenIndex, endTokenIndex, false));
235842
+ _base.state.scopes.push(new (0, _state.Scope)(nameScopeStartTokenIndex, endTokenIndex, false));
235843
235843
  _base.state.scopeDepth--;
235844
235844
  }
235845
235845
  }
@@ -249426,8 +249426,8 @@ ${RUN_USAGE}
249426
249426
 
249427
249427
  // scripts/lib/runtime-build/build-workspace.mjs
249428
249428
  init_define_globalThis_DIAGONAL_SDK_RUNTIME();
249429
- import { readFileSync as readFileSync7, readdirSync as readdirSync3 } from "node:fs";
249430
- import { basename as basename2, extname, join as join9, relative as relative3, sep } from "node:path";
249429
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync7, readdirSync as readdirSync3, writeFileSync as writeFileSync2 } from "node:fs";
249430
+ import { basename as basename3, dirname as dirname6, extname as extname2, join as join9, relative as relative3, sep } from "node:path";
249431
249431
 
249432
249432
  // scripts/lib/runtime-build/capabilities.mjs
249433
249433
  init_define_globalThis_DIAGONAL_SDK_RUNTIME();
@@ -266857,7 +266857,7 @@ function isTypeOnlyStatement(statement2) {
266857
266857
  }
266858
266858
  return statement2.declaration?.type === "TSTypeAliasDeclaration" || statement2.declaration?.type === "TSInterfaceDeclaration";
266859
266859
  }
266860
- function defaultExportOf(ast, { allowHelpers = false, allowAppImports = false } = {}) {
266860
+ function defaultExportOf(ast, { allowHelpers = false, allowAppImports = false, extraImports = [] } = {}) {
266861
266861
  const imports = [];
266862
266862
  const helpers = [];
266863
266863
  let declaration = null;
@@ -266869,9 +266869,10 @@ function defaultExportOf(ast, { allowHelpers = false, allowAppImports = false }
266869
266869
  if (allowAppImports && refused !== null) {
266870
266870
  throw new SourceError(refused, statement2.source);
266871
266871
  }
266872
- if (!isAuthoringModule(specifier) && !allowAppImports) {
266872
+ if (!isAuthoringModule(specifier) && !allowAppImports && !extraImports.includes(specifier)) {
266873
+ const allowed = [AUTHORING_MODULE, ...extraImports].map((name) => `"${name}"`).join(" and ");
266873
266874
  throw new SourceError(
266874
- `Import from "${specifier}" is not allowed. Only "${AUTHORING_MODULE}" can be imported`,
266875
+ `Import from "${specifier}" is not allowed. Only ${allowed} can be imported`,
266875
266876
  statement2
266876
266877
  );
266877
266878
  }
@@ -266989,7 +266990,7 @@ function readCapabilities(capabilities, node) {
266989
266990
  }
266990
266991
  return capabilities;
266991
266992
  }
266992
- function relationPathProblem(resource, path, resourcesById) {
266993
+ function relationPathProblem(resource, path, resourcesBySlug, platformSlugs) {
266993
266994
  let current = resource;
266994
266995
  for (const [position2, key] of path.entries()) {
266995
266996
  if (typeof key !== "string") {
@@ -267005,15 +267006,15 @@ function relationPathProblem(resource, path, resourcesById) {
267005
267006
  if (field2.type !== "relation") {
267006
267007
  return `"${path.slice(0, position2 + 1).join(".")}" is not a relation`;
267007
267008
  }
267008
- current = resourcesById.get(field2.settings.resource_id);
267009
+ current = resourcesBySlug.get(field2.settings.resource);
267009
267010
  if (current === void 0) {
267010
- return `"${path.slice(0, position2 + 1).join(".")}" relates to a resource outside the workspace files`;
267011
+ return platformSlugs.has(field2.settings.resource) ? `"${path.slice(0, position2 + 1).join(".")}" relates to a platform resource, whose fields the build does not know` : `"${path.slice(0, position2 + 1).join(".")}" relates to a resource outside the workspace files`;
267011
267012
  }
267012
267013
  }
267013
267014
  return null;
267014
267015
  }
267015
267016
  function checkSetting(context, setting, value, label, node) {
267016
- const { resource, fieldsByKey, resourcesById, problems } = context;
267017
+ const { resource, fieldsByKey, resourcesBySlug, platformSlugs, problems } = context;
267017
267018
  if (value === null || value === void 0) {
267018
267019
  if (setting.required) {
267019
267020
  problems.push({ message: `${label} is required`, node });
@@ -267035,7 +267036,7 @@ function checkSetting(context, setting, value, label, node) {
267035
267036
  return;
267036
267037
  }
267037
267038
  if (setting.path) {
267038
- const problem = Array.isArray(value) ? relationPathProblem(resource, value, resourcesById) : "must be a list of field keys";
267039
+ const problem = Array.isArray(value) ? relationPathProblem(resource, value, resourcesBySlug, platformSlugs) : "must be a list of field keys";
267039
267040
  if (problem !== null) {
267040
267041
  problems.push({ message: `${label} ${problem}`, node });
267041
267042
  }
@@ -267089,13 +267090,14 @@ function checkSettings(context, settings, values, label, node, refused = {}) {
267089
267090
  checkSetting(context, setting, values[key], `${label}.${key}`, propertyNode(node, key));
267090
267091
  }
267091
267092
  }
267092
- function checkCapabilities(resources, diagnostics) {
267093
- const resourcesById = new Map(resources.map((resource) => [resource.id, resource]));
267093
+ function checkCapabilities(resources, platformSlugs, diagnostics) {
267094
+ const resourcesBySlug = new Map(resources.map((resource) => [resource.slug, resource]));
267094
267095
  for (const resource of resources) {
267095
267096
  const context = {
267096
267097
  resource,
267097
267098
  fieldsByKey: new Map(resource.fields.map((field2) => [field2.key, field2])),
267098
- resourcesById,
267099
+ resourcesBySlug,
267100
+ platformSlugs,
267099
267101
  problems: []
267100
267102
  };
267101
267103
  for (const [identifier4, values] of Object.entries(resource.capabilities)) {
@@ -294982,16 +294984,17 @@ function resourceCatalog2(resources) {
294982
294984
  for (const resource of resources) {
294983
294985
  const fields = {};
294984
294986
  for (const field2 of resource.fields) {
294987
+ const { resource: resource2, ...settings } = field2.settings;
294985
294988
  fields[field2.key] = {
294986
294989
  key: field2.key,
294987
294990
  type: field2.type,
294988
294991
  label: field2.label,
294989
294992
  is_required: field2.required,
294990
- settings: field2.settings
294993
+ settings: field2.type === "relation" ? { ...settings, resource_id: resource2 } : settings
294991
294994
  };
294992
294995
  }
294993
- catalog[resource.id] = {
294994
- id: resource.id,
294996
+ catalog[resource.slug] = {
294997
+ id: resource.slug,
294995
294998
  slug: resource.slug,
294996
294999
  name: resource.name,
294997
295000
  singular_name: resource.singular_name,
@@ -295081,94 +295084,116 @@ function lintUnit(unit, catalogs) {
295081
295084
  return [];
295082
295085
  }
295083
295086
 
295084
- // scripts/lib/runtime-build/resources.mjs
295085
- init_define_globalThis_DIAGONAL_SDK_RUNTIME();
295086
-
295087
- // node_modules/uuid/dist-node/index.js
295087
+ // scripts/lib/runtime-build/resource-references.mjs
295088
295088
  init_define_globalThis_DIAGONAL_SDK_RUNTIME();
295089
-
295090
- // node_modules/uuid/dist-node/stringify.js
295091
- init_define_globalThis_DIAGONAL_SDK_RUNTIME();
295092
- var byteToHex = [];
295093
- for (let i = 0; i < 256; ++i) {
295094
- byteToHex.push((i + 256).toString(16).slice(1));
295089
+ import { basename as basename2, extname } from "node:path";
295090
+ var GENERATED_RESOURCES_PATH = "generated/resources.ts";
295091
+ var GENERATED_RESOURCES_MODULE = "@app/generated/resources";
295092
+ var RESOURCES_EXPORT = "Resources";
295093
+ var SLUG_PATTERN = /^[a-z][a-z0-9_-]*$/;
295094
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
295095
+ function resourceConstName(slug) {
295096
+ return slug.split(/[-_]+/).filter((word) => word !== "").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
295095
295097
  }
295096
- function unsafeStringify(arr, offset = 0) {
295097
- return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
295098
+ function resourceNames(paths, diagnostics) {
295099
+ const slugsByName = /* @__PURE__ */ new Map();
295100
+ const namesBySlug = /* @__PURE__ */ new Map();
295101
+ for (const path of paths) {
295102
+ const slug = basename2(path, extname(path));
295103
+ if (!SLUG_PATTERN.test(slug)) {
295104
+ diagnostics.push(
295105
+ diagnosticAt(
295106
+ path,
295107
+ null,
295108
+ `The file name "${slug}" is the resource slug, so it must start with a lowercase letter and contain only a-z, 0-9, _ and -`
295109
+ )
295110
+ );
295111
+ continue;
295112
+ }
295113
+ const name = resourceConstName(slug);
295114
+ const other = slugsByName.get(name);
295115
+ if (other !== void 0) {
295116
+ diagnostics.push(
295117
+ diagnosticAt(
295118
+ path,
295119
+ null,
295120
+ `The slugs "${other}" and "${slug}" both have the name Resources.${name}. Rename one of the files`
295121
+ )
295122
+ );
295123
+ continue;
295124
+ }
295125
+ slugsByName.set(name, slug);
295126
+ namesBySlug.set(slug, name);
295127
+ }
295128
+ return { slugsByName, namesBySlug };
295098
295129
  }
295099
-
295100
- // node_modules/uuid/dist-node/rng.js
295101
- init_define_globalThis_DIAGONAL_SDK_RUNTIME();
295102
- var rnds8 = new Uint8Array(16);
295103
- function rng() {
295104
- return crypto.getRandomValues(rnds8);
295130
+ function generatedResourcesSource({ slugsByName }) {
295131
+ const names2 = [...slugsByName.keys()].toSorted(
295132
+ (left, right) => left < right ? -1 : left > right ? 1 : 0
295133
+ );
295134
+ const body = names2.length === 0 ? "{}" : `{
295135
+ ${names2.map((name) => ` ${name}: '${slugsByName.get(name)}'`).join(",\n")}
295136
+ }`;
295137
+ return `// AUTO-GENERATED by \`diagonal check\`. Do not edit.
295138
+ export const ${RESOURCES_EXPORT} = ${body} as const;
295139
+ `;
295105
295140
  }
295106
-
295107
- // node_modules/uuid/dist-node/v7.js
295108
- init_define_globalThis_DIAGONAL_SDK_RUNTIME();
295109
- var _state = {};
295110
- function v7(options2, buf, offset) {
295111
- let bytes;
295112
- if (options2) {
295113
- bytes = v7Bytes(options2.random ?? options2.rng?.() ?? rng(), options2.msecs, options2.seq, buf, offset);
295114
- } else {
295115
- const now = Date.now();
295116
- const rnds = rng();
295117
- updateV7State(_state, now, rnds);
295118
- bytes = v7Bytes(rnds, _state.msecs, _state.seq, buf, offset);
295119
- }
295120
- return buf ?? unsafeStringify(bytes);
295121
- }
295122
- function updateV7State(state, now, rnds) {
295123
- state.msecs ??= -Infinity;
295124
- state.seq ??= 0;
295125
- if (now > state.msecs) {
295126
- state.seq = v7Sequence(rnds);
295127
- state.msecs = now;
295128
- } else {
295129
- state.seq = state.seq + 1 | 0;
295130
- if (state.seq === 0) {
295131
- state.msecs++;
295141
+ function resourcesBinding(ast, names2) {
295142
+ for (const statement2 of ast.program.body) {
295143
+ if (statement2.type !== "ImportDeclaration" || statement2.source.value !== GENERATED_RESOURCES_MODULE || statement2.importKind === "type") {
295144
+ continue;
295145
+ }
295146
+ for (const specifier of statement2.specifiers) {
295147
+ if (specifier.type === "ImportSpecifier" && specifier.importKind !== "type" && (specifier.imported.name ?? specifier.imported.value) === RESOURCES_EXPORT) {
295148
+ return { local: specifier.local.name, slugsByName: names2.slugsByName };
295149
+ }
295132
295150
  }
295133
295151
  }
295134
- return state;
295152
+ return null;
295135
295153
  }
295136
- function v7Bytes(rnds, msecs, seq, buf, offset = 0) {
295137
- if (rnds.length < 16) {
295138
- throw new Error("Random bytes length must be >= 16");
295154
+ function referenceProblem(reference, resources, { namesBySlug }) {
295155
+ if (UUID_PATTERN.test(reference)) {
295156
+ return {
295157
+ kind: "id",
295158
+ message: `"${reference}" is a resource id. Refer to a resource by its slug, with Resources.<Name> from ${GENERATED_RESOURCES_MODULE}`
295159
+ };
295139
295160
  }
295140
- if (!buf) {
295141
- buf = new Uint8Array(16);
295142
- offset = 0;
295143
- } else {
295144
- if (offset < 0 || offset + 16 > buf.length) {
295145
- throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
295146
- }
295147
- }
295148
- msecs ??= Date.now();
295149
- seq ??= v7Sequence(rnds);
295150
- buf[offset++] = msecs / 1099511627776 & 255;
295151
- buf[offset++] = msecs / 4294967296 & 255;
295152
- buf[offset++] = msecs / 16777216 & 255;
295153
- buf[offset++] = msecs / 65536 & 255;
295154
- buf[offset++] = msecs / 256 & 255;
295155
- buf[offset++] = msecs & 255;
295156
- buf[offset++] = 112 | seq >>> 28 & 15;
295157
- buf[offset++] = seq >>> 20 & 255;
295158
- buf[offset++] = 128 | seq >>> 14 & 63;
295159
- buf[offset++] = seq >>> 6 & 255;
295160
- buf[offset++] = seq << 2 & 255 | rnds[10] & 3;
295161
- buf[offset++] = rnds[11];
295162
- buf[offset++] = rnds[12];
295163
- buf[offset++] = rnds[13];
295164
- buf[offset++] = rnds[14];
295165
- buf[offset++] = rnds[15];
295166
- return buf;
295167
- }
295168
- function v7Sequence(rnds) {
295169
- return (rnds[6] & 127) << 24 | rnds[7] << 16 | rnds[8] << 8 | rnds[9];
295170
- }
295171
- var v7_default = v7;
295161
+ const named = resources.find(
295162
+ (resource) => resource.name === reference || resource.singular_name === reference
295163
+ );
295164
+ if (named !== void 0) {
295165
+ return {
295166
+ kind: "name",
295167
+ message: `"${reference}" is a name of the resource "${named.slug}". Use Resources.${namesBySlug.get(named.slug)}`
295168
+ };
295169
+ }
295170
+ return {
295171
+ kind: "unknown",
295172
+ message: `Unknown resource "${reference}". Use Resources.<Name> from ${GENERATED_RESOURCES_MODULE}`
295173
+ };
295174
+ }
295175
+ function slugStringWarnings(path, node, { namesBySlug }, options2 = {}) {
295176
+ const warnings2 = [];
295177
+ const pending = [node];
295178
+ while (pending.length > 0) {
295179
+ const current = pending.pop();
295180
+ if (current.type === "ObjectProperty" && !current.computed && (current.key.name ?? current.key.value) === "resource" && current.value.type === "StringLiteral" && namesBySlug.has(current.value.value)) {
295181
+ warnings2.push(
295182
+ diagnosticAt(
295183
+ path,
295184
+ current.value,
295185
+ `Use Resources.${namesBySlug.get(current.value.value)} from ${GENERATED_RESOURCES_MODULE} in place of the string "${current.value.value}"`,
295186
+ { ...options2, severity: "warning" }
295187
+ )
295188
+ );
295189
+ }
295190
+ pending.push(...childNodes(current));
295191
+ }
295192
+ return warnings2;
295193
+ }
295194
+
295195
+ // scripts/lib/runtime-build/resources.mjs
295196
+ init_define_globalThis_DIAGONAL_SDK_RUNTIME();
295172
295197
 
295173
295198
  // scripts/lib/runtime-build/fields.mjs
295174
295199
  init_define_globalThis_DIAGONAL_SDK_RUNTIME();
@@ -296890,6 +296915,20 @@ function fieldBuilderCall(node, options2) {
296890
296915
  }
296891
296916
  return new FieldBuilderValue(callee.property.name, builderOptions, node);
296892
296917
  }
296918
+ function resourceReference(node, options2) {
296919
+ const binding = options2.resources ?? null;
296920
+ if (binding === null || node.computed || node.object.type !== "Identifier" || node.object.name !== binding.local || node.property.type !== "Identifier") {
296921
+ throw new SourceError("MemberExpression is not allowed in static data. Use literal values only", node);
296922
+ }
296923
+ const slug = binding.slugsByName.get(node.property.name);
296924
+ if (slug === void 0) {
296925
+ throw new SourceError(
296926
+ `Unknown resource "${binding.local}.${node.property.name}". Each name in ${binding.local} is a file in resources/`,
296927
+ node.property
296928
+ );
296929
+ }
296930
+ return slug;
296931
+ }
296893
296932
  function staticValue(node, options2 = {}) {
296894
296933
  switch (node.type) {
296895
296934
  case "StringLiteral":
@@ -296946,6 +296985,8 @@ function staticValue(node, options2 = {}) {
296946
296985
  }
296947
296986
  case "CallExpression":
296948
296987
  return fieldBuilderCall(node, options2);
296988
+ case "MemberExpression":
296989
+ return resourceReference(node, options2);
296949
296990
  case "Identifier":
296950
296991
  throw new SourceError(
296951
296992
  `"${node.name}" is a variable reference. Only literal values are allowed in static data`,
@@ -297243,7 +297284,7 @@ function relationSettings(key, builder2, typeCatalog, settings) {
297243
297284
  const { resource_id: resource, type: relationType, ...rest } = settings;
297244
297285
  if (typeof resource !== "string" || resource === "") {
297245
297286
  throw new SourceError(
297246
- `field.relation "${key}" needs "resource": '<resource id or slug>'`,
297287
+ `field.relation "${key}" needs "resource": Resources.<Name>`,
297247
297288
  optionNode(builder2, "resource")
297248
297289
  );
297249
297290
  }
@@ -297262,7 +297303,7 @@ function relationSettings(key, builder2, typeCatalog, settings) {
297262
297303
  typeCatalog,
297263
297304
  (...path) => optionNode(builder2, "settings", "inverse", ...path)
297264
297305
  );
297265
- return { delete_policy: "ignore", ...rest, resource_id: resource, type: relationType };
297306
+ return { delete_policy: "ignore", ...rest, resource, type: relationType };
297266
297307
  }
297267
297308
  function buildField(key, builder2, keyNode) {
297268
297309
  if (!FIELD_KEY_PATTERN.test(key)) {
@@ -297368,8 +297409,6 @@ function buildField(key, builder2, keyNode) {
297368
297409
  }
297369
297410
 
297370
297411
  // scripts/lib/runtime-build/resources.mjs
297371
- var UUID_V7_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
297372
- var SLUG_PATTERN = /^[a-z][a-z0-9_-]*$/;
297373
297412
  var POLICY_SCOPES = [
297374
297413
  "index",
297375
297414
  "show",
@@ -297384,8 +297423,6 @@ var POLICY_SCOPES = [
297384
297423
  ];
297385
297424
  var POLICY_ACCESS = /* @__PURE__ */ new Set(["public", "protected"]);
297386
297425
  var RESOURCE_KEYS = /* @__PURE__ */ new Set([
297387
- "id",
297388
- "slug",
297389
297426
  "name",
297390
297427
  "singularName",
297391
297428
  "displayField",
@@ -297583,27 +297620,34 @@ function buildDataApi(dataApi, node) {
297583
297620
  }
297584
297621
  return { endpoints };
297585
297622
  }
297586
- function buildResource(path, definitionNode) {
297587
- const { value, nodes } = staticObject(definitionNode, { fieldBuilders: true });
297588
- for (const key of Object.keys(value)) {
297589
- if (!RESOURCE_KEYS.has(key)) {
297590
- throw new SourceError(`Unknown resource option "${key}"`, nodes[key]);
297591
- }
297623
+ function platformResource(path, slug, value, nodes) {
297624
+ if (value.platform !== true) {
297625
+ throw new SourceError('"platform" must be true', nodes.platform);
297592
297626
  }
297593
- const id = requireString(value, "id", nodes.id ?? definitionNode);
297594
- if (!UUID_V7_PATTERN.test(id)) {
297627
+ const other = Object.keys(value).find((key) => key !== "platform");
297628
+ if (other !== void 0) {
297595
297629
  throw new SourceError(
297596
- `Resource id "${id}" must be a lowercase UUID v7
297597
- Use a new v7 id, for example '${v7_default()}'`,
297598
- nodes.id
297630
+ `A platform resource has only "platform: true". The platform owns its "${other}"`,
297631
+ nodes[other]
297599
297632
  );
297600
297633
  }
297601
- const slug = requireString(value, "slug", nodes.slug ?? definitionNode);
297602
- if (!SLUG_PATTERN.test(slug)) {
297603
- throw new SourceError(
297604
- `Resource slug "${slug}" must start with a lowercase letter and contain only a-z, 0-9, _ and -`,
297605
- nodes.slug
297606
- );
297634
+ return { path, slug, platform: true };
297635
+ }
297636
+ function buildResource(path, definitionNode, { slug, resources }) {
297637
+ const { value, nodes } = staticObject(definitionNode, { fieldBuilders: true, resources });
297638
+ if (Object.hasOwn(value, "platform")) {
297639
+ return platformResource(path, slug, value, nodes);
297640
+ }
297641
+ for (const key of Object.keys(value)) {
297642
+ if (key === "id" || key === "slug") {
297643
+ throw new SourceError(
297644
+ `Remove "${key}". The file name is the resource slug, and the platform derives the id`,
297645
+ nodes[key]
297646
+ );
297647
+ }
297648
+ if (!RESOURCE_KEYS.has(key)) {
297649
+ throw new SourceError(`Unknown resource option "${key}"`, nodes[key]);
297650
+ }
297607
297651
  }
297608
297652
  const name = requireString(value, "name", nodes.name ?? definitionNode);
297609
297653
  const singularName = requireString(value, "singularName", nodes.singularName ?? definitionNode);
@@ -297636,7 +297680,6 @@ Use a new v7 id, for example '${v7_default()}'`,
297636
297680
  const embeddings = buildEmbeddings(value.embeddings, nodes.embeddings);
297637
297681
  return {
297638
297682
  path,
297639
- id,
297640
297683
  slug,
297641
297684
  name,
297642
297685
  singular_name: singularName,
@@ -297661,14 +297704,14 @@ function humanize3(key) {
297661
297704
  return words.charAt(0).toUpperCase() + words.slice(1);
297662
297705
  }
297663
297706
  function addInverseFields(resources, diagnostics) {
297664
- const resourcesById = new Map(resources.map((resource) => [resource.id, resource]));
297707
+ const resourcesBySlug = new Map(resources.map((resource) => [resource.slug, resource]));
297665
297708
  for (const resource of resources) {
297666
297709
  for (const field2 of resource.fields) {
297667
297710
  const inverse = field2.type === "relation" ? field2.settings.inverse ?? null : null;
297668
297711
  if (inverse === null || inverse === false) {
297669
297712
  continue;
297670
297713
  }
297671
- const target = resourcesById.get(field2.settings.resource_id);
297714
+ const target = resourcesBySlug.get(field2.settings.resource);
297672
297715
  if (target === void 0) {
297673
297716
  continue;
297674
297717
  }
@@ -297688,7 +297731,7 @@ function addInverseFields(resources, diagnostics) {
297688
297731
  const settings = {
297689
297732
  delete_policy: "ignore",
297690
297733
  ...inverse.settings,
297691
- resource_id: resource.id,
297734
+ resource: resource.slug,
297692
297735
  type,
297693
297736
  inverse: false
297694
297737
  };
@@ -297718,12 +297761,12 @@ function addInverseFields(resources, diagnostics) {
297718
297761
  computed: null,
297719
297762
  guard: [],
297720
297763
  settings,
297721
- inverse_of: { resource_id: resource.id, key: field2.key }
297764
+ inverse_of: { resource: resource.slug, key: field2.key }
297722
297765
  });
297723
297766
  }
297724
297767
  }
297725
297768
  }
297726
- function fieldPathProblem(resource, path, resourcesById, { relations }) {
297769
+ function fieldPathProblem(resource, path, resourcesBySlug, { relations }) {
297727
297770
  let fields = resource.fields;
297728
297771
  const segments = path.split(".");
297729
297772
  for (const [position2, segment] of segments.entries()) {
@@ -297743,9 +297786,9 @@ function fieldPathProblem(resource, path, resourcesById, { relations }) {
297743
297786
  if (last2) {
297744
297787
  return `"${prefix2}" is a relation. Name a field of the related resource, for example "${prefix2}.name"`;
297745
297788
  }
297746
- const target = resourcesById.get(field2.settings.resource_id);
297789
+ const target = resourcesBySlug.get(field2.settings.resource);
297747
297790
  if (target === void 0) {
297748
- return `"${prefix2}" relates to a resource outside the workspace files`;
297791
+ return `"${prefix2}" relates to a platform resource, whose fields the build does not know`;
297749
297792
  }
297750
297793
  fields = target.fields;
297751
297794
  continue;
@@ -297767,7 +297810,7 @@ function fieldPathProblem(resource, path, resourcesById, { relations }) {
297767
297810
  return null;
297768
297811
  }
297769
297812
  function checkFieldPaths(resources, diagnostics) {
297770
- const resourcesById = new Map(resources.map((resource) => [resource.id, resource]));
297813
+ const resourcesBySlug = new Map(resources.map((resource) => [resource.slug, resource]));
297771
297814
  for (const resource of resources) {
297772
297815
  const checks = [
297773
297816
  ["searchableFields", resource.searchable_fields, true],
@@ -297775,7 +297818,7 @@ function checkFieldPaths(resources, diagnostics) {
297775
297818
  ];
297776
297819
  for (const [option, paths, relations] of checks) {
297777
297820
  for (const path of paths) {
297778
- const problem = fieldPathProblem(resource, path, resourcesById, { relations });
297821
+ const problem = fieldPathProblem(resource, path, resourcesBySlug, { relations });
297779
297822
  if (problem !== null) {
297780
297823
  const node = option === "searchableFields" ? resource.pathNodes.searchableFields : resource.pathNodes.embeddings;
297781
297824
  diagnostics.push(diagnosticAt(resource.path, node, `${option}: ${problem}`));
@@ -298581,7 +298624,7 @@ function pageAuthorizer(meta, nodes) {
298581
298624
  const resource = value.resource ?? null;
298582
298625
  const permissions = value.permissions ?? [];
298583
298626
  if (resource !== null && (typeof resource !== "string" || resource === "")) {
298584
- throw new SourceError('"authorizer.resource" must be a resource slug or id', nodes.authorizer);
298627
+ throw new SourceError('"authorizer.resource" must be Resources.<Name> or a resource slug', nodes.authorizer);
298585
298628
  }
298586
298629
  if (!Array.isArray(permissions) || permissions.some((permission) => typeof permission !== "string" || permission === "")) {
298587
298630
  throw new SourceError(
@@ -298618,7 +298661,10 @@ function pageParams(meta, nodes, route) {
298618
298661
  }
298619
298662
  const { resource = null, lookupField = null, relations = [], scope: scope2 = null } = binding;
298620
298663
  if (typeof resource !== "string" || resource === "") {
298621
- throw new SourceError(`"params.${key}.resource" must be a resource slug or id`, nodes.params);
298664
+ throw new SourceError(
298665
+ `"params.${key}.resource" must be Resources.<Name> or a resource slug`,
298666
+ nodes.params
298667
+ );
298622
298668
  }
298623
298669
  if (lookupField !== null && (typeof lookupField !== "string" || lookupField === "")) {
298624
298670
  throw new SourceError(`"params.${key}.lookupField" must be a field key`, nodes.params);
@@ -298636,13 +298682,13 @@ function pageParams(meta, nodes, route) {
298636
298682
  }
298637
298683
  return params;
298638
298684
  }
298639
- function buildPage(path, source, ast, root = null) {
298685
+ function buildPage(path, source, ast, root = null, resources = null) {
298640
298686
  const { declaration, helpers } = defaultExportOf(ast, {
298641
298687
  allowHelpers: true,
298642
298688
  allowAppImports: true
298643
298689
  });
298644
298690
  const [metaNode, functionNode] = expectDefineCall(declaration, "definePage", 2);
298645
- const { value: meta, nodes } = staticObject(metaNode);
298691
+ const { value: meta, nodes } = staticObject(metaNode, { resources });
298646
298692
  for (const key of Object.keys(meta)) {
298647
298693
  if (!PAGE_KEYS.has(key)) {
298648
298694
  throw new SourceError(`Unknown page option "${key}"`, nodes[key]);
@@ -298681,7 +298727,12 @@ function buildPage(path, source, ast, root = null) {
298681
298727
  authorizer: pageAuthorizer(meta, nodes),
298682
298728
  params: pageParams(meta, nodes, meta.route),
298683
298729
  ...functionUnit(source, ast, functionNode, helpers, appImports(root, path, source, ast)),
298684
- layoutNode: nodes.layout ?? null
298730
+ layoutNode: nodes.layout ?? null,
298731
+ resourceNodes: {
298732
+ meta: metaNode,
298733
+ authorizer: nodes.authorizer ?? null,
298734
+ params: nodes.params ?? null
298735
+ }
298685
298736
  };
298686
298737
  }
298687
298738
 
@@ -298751,114 +298802,6 @@ function scriptSdkRuntimeFiles() {
298751
298802
  return sdkRuntimeFiles;
298752
298803
  }
298753
298804
 
298754
- // scripts/lib/runtime-build/workspace-script-types.mjs
298755
- init_define_globalThis_DIAGONAL_SDK_RUNTIME();
298756
- var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
298757
- var CONTENT_TYPE = "content";
298758
- var NUMBER_TYPES = /* @__PURE__ */ new Set(["number"]);
298759
- var BOOLEAN_TYPES = /* @__PURE__ */ new Set(["boolean"]);
298760
- var ARRAY_TYPES = /* @__PURE__ */ new Set(["list"]);
298761
- var OBJECT_TYPES = /* @__PURE__ */ new Set([
298762
- "dictionary",
298763
- "event_schedule",
298764
- "form",
298765
- "guard",
298766
- "identity",
298767
- "location",
298768
- "money",
298769
- "point",
298770
- "record",
298771
- "regular_hours",
298772
- "relation"
298773
- ]);
298774
- function scriptResourceName(resourceName) {
298775
- return resourceName.replaceAll(/[^A-Za-z0-9]+/g, " ").split(/\s+/).filter((word) => word !== "").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
298776
- }
298777
- function fieldTsType(fieldType) {
298778
- if (fieldType === CONTENT_TYPE) {
298779
- return "ContentDocument";
298780
- }
298781
- if (NUMBER_TYPES.has(fieldType)) {
298782
- return "number";
298783
- }
298784
- if (BOOLEAN_TYPES.has(fieldType)) {
298785
- return "boolean";
298786
- }
298787
- if (ARRAY_TYPES.has(fieldType)) {
298788
- return "unknown[]";
298789
- }
298790
- if (OBJECT_TYPES.has(fieldType)) {
298791
- return "Record<string, unknown>";
298792
- }
298793
- return "string";
298794
- }
298795
- function propertyKey2(name) {
298796
- return IDENTIFIER.test(name) ? name : JSON.stringify(name);
298797
- }
298798
- function byteOrder(left, right) {
298799
- return left < right ? -1 : left > right ? 1 : 0;
298800
- }
298801
- function scriptResourcesByName(resources) {
298802
- const resourcesByName = /* @__PURE__ */ new Map();
298803
- const duplicates = [];
298804
- for (const resource of resources) {
298805
- const name = scriptResourceName(resource.name);
298806
- if (resourcesByName.has(name)) {
298807
- duplicates.push({ name, resource });
298808
- continue;
298809
- }
298810
- resourcesByName.set(name, resource);
298811
- }
298812
- return { resourcesByName, duplicates };
298813
- }
298814
- function scriptResourceValues(resourcesByName) {
298815
- return Object.fromEntries(
298816
- [...resourcesByName.keys()].toSorted(byteOrder).map((name) => [name, resourcesByName.get(name).id])
298817
- );
298818
- }
298819
- function scriptWorkspaceDeclarations(resourcesByName) {
298820
- const names2 = [...resourcesByName.keys()].toSorted(byteOrder);
298821
- const values = [];
298822
- const types2 = [];
298823
- let usesContent = false;
298824
- for (const name of names2) {
298825
- const resource = resourcesByName.get(name);
298826
- const input = [];
298827
- const record = ["id: string"];
298828
- for (const field2 of resource.fields) {
298829
- const type = fieldTsType(field2.type);
298830
- usesContent = usesContent || type === "ContentDocument";
298831
- const key = propertyKey2(field2.key);
298832
- input.push(`${key}?: ${type} | null`);
298833
- record.push(`${key}: ${type} | null`);
298834
- }
298835
- const resourceKey = propertyKey2(name);
298836
- const capabilities = Object.keys(resource.capabilities ?? {}).toSorted(byteOrder).map((capability) => `'${capability}'`);
298837
- values.push(` ${resourceKey}: '${resource.id}',`);
298838
- types2.push(
298839
- ` ${resourceKey}: { id: '${resource.id}'; capabilities: ${capabilities.length === 0 ? "never" : capabilities.join(" | ")}; input: { ${input.join("; ")} }; record: { ${record.join("; ")} } & Record<string, unknown> };`
298840
- );
298841
- }
298842
- return [
298843
- "// AUTO-GENERATED for one workspace. Refresh with `diagonal-workflow pull-types`.",
298844
- ...usesContent ? ["import type { ContentDocument } from '@diagonal/workflow';", ""] : [],
298845
- "export const resources = {",
298846
- ...values,
298847
- "} as const;",
298848
- "",
298849
- "declare module '@diagonal/workflow' {",
298850
- " interface WorkspaceResources {",
298851
- ...types2,
298852
- " }",
298853
- "}",
298854
- ""
298855
- ].join("\n");
298856
- }
298857
- function scriptWorkspaceModule(values) {
298858
- return `export const resources = Object.freeze(${JSON.stringify(values)});
298859
- `;
298860
- }
298861
-
298862
298805
  // scripts/lib/runtime-build/workflow-tests.mjs
298863
298806
  var WORKFLOW_TEST_STAGE = "workflow test";
298864
298807
  var WORKFLOW_TEST_SUFFIX = ".test.ts";
@@ -298981,7 +298924,6 @@ function parseResults(stdout) {
298981
298924
  function runWorkflowTests({
298982
298925
  tests,
298983
298926
  workflows,
298984
- resources,
298985
298927
  root: workspaceRoot = null,
298986
298928
  appModules = /* @__PURE__ */ new Map()
298987
298929
  }) {
@@ -299029,7 +298971,7 @@ function runWorkflowTests({
299029
298971
  for (const [path, content] of Object.entries(scriptSdkRuntimeFiles())) {
299030
298972
  writeFile(root, `sdk/${path}`, content);
299031
298973
  }
299032
- writeFile(root, "sdk/workspace.js", scriptWorkspaceModule(resources));
298974
+ writeFile(root, "sdk/workspace.js", "export {};\n");
299033
298975
  writeFile(root, "sdk/runtime.js", AUTHORING_HELPERS_MODULE);
299034
298976
  for (const [path, module] of modules) {
299035
298977
  const resolveSdk = sdkSpecifier(sandboxPrefix(path));
@@ -299308,7 +299250,7 @@ function builder(method, call, checker, visiting) {
299308
299250
  if (!type?.isStringLiteral()) {
299309
299251
  throw new SchemaError(
299310
299252
  call,
299311
- "v.record() needs a resource from @diagonal/workflow/workspace, such as resources.Products."
299253
+ "v.record() needs a resource, such as resources.Products from @diagonal/workflow/workspace or Resources.Products from @app/generated/resources in a file-based workspace."
299312
299254
  );
299313
299255
  }
299314
299256
  return base("record", { resource: type.value });
@@ -299318,7 +299260,7 @@ function builder(method, call, checker, visiting) {
299318
299260
  if (!type?.isStringLiteral()) {
299319
299261
  throw new SchemaError(
299320
299262
  call,
299321
- "v.recordReference() needs a resource from @diagonal/workflow/workspace, such as resources.Products."
299263
+ "v.recordReference() needs a resource, such as resources.Products from @diagonal/workflow/workspace or Resources.Products from @app/generated/resources in a file-based workspace."
299322
299264
  );
299323
299265
  }
299324
299266
  return base("recordReference", {
@@ -300007,7 +299949,7 @@ function compileWorkflowScript(request2, scriptProgram = createScriptProgram(req
300007
299949
  // scripts/lib/runtime-build/workflow-config.mjs
300008
299950
  init_define_globalThis_DIAGONAL_SDK_RUNTIME();
300009
299951
  var WORKFLOW_STAGE = "workflow";
300010
- var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
299952
+ var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
300011
299953
  var ROUTE_PATTERN = /^\/[a-z0-9\-_/{}]*$/;
300012
299954
  var ROUTE_PARAM_PATTERN = /\{([^}]+)\}/g;
300013
299955
  var CRON_FIELD_PATTERN = /^[0-9A-Za-z*/,\-?#]+$/;
@@ -300177,7 +300119,7 @@ function readWorkflowConfig(path, configNode, context) {
300177
300119
  const report = (node, message) => diagnostics.push(diagnosticAt(path, node, message, { stage: WORKFLOW_STAGE }));
300178
300120
  let value;
300179
300121
  try {
300180
- value = staticValue(configNode);
300122
+ value = staticValue(configNode, { resources: context.resourcesBinding });
300181
300123
  } catch (error) {
300182
300124
  if (!(error instanceof SourceError)) {
300183
300125
  throw error;
@@ -300226,13 +300168,13 @@ function readWorkflowConfig(path, configNode, context) {
300226
300168
  }
300227
300169
  function resolveResource(reference, node, context, report) {
300228
300170
  if (typeof reference !== "string" || reference === "") {
300229
- report(node, '"resource" must be the slug, name, or id of a resource');
300171
+ report(node, '"resource" must be Resources.<Name>');
300230
300172
  return null;
300231
300173
  }
300232
300174
  const resource = context.findResource(reference);
300233
300175
  if (resource === void 0) {
300234
300176
  if (context.reportUnknownResources) {
300235
- report(node, `Unknown resource "${reference}". Use the slug, name, or id of a resource`);
300177
+ report(node, referenceProblem(reference, context.resources, context.names).message);
300236
300178
  }
300237
300179
  return null;
300238
300180
  }
@@ -300304,12 +300246,12 @@ function triggerSettings(trigger, at, context, fail) {
300304
300246
  if (resource === null || event === null) {
300305
300247
  return null;
300306
300248
  }
300307
- return trigger.filters === void 0 ? { event, resource_id: resource.id } : { event, resource_id: resource.id, filters: trigger.filters };
300249
+ return trigger.filters === void 0 ? { event, resource: resource.slug } : { event, resource: resource.slug, filters: trigger.filters };
300308
300250
  }
300309
300251
  case "recordLifecycle": {
300310
300252
  const resource = resolveResource(trigger.resource, at("resource"), context, fail);
300311
300253
  const hook = oneOf(trigger, "hook", LIFECYCLE_HOOKS, at, fail);
300312
- return resource === null || hook === null ? null : { hook, resource_id: resource.id };
300254
+ return resource === null || hook === null ? null : { hook, resource: resource.slug };
300313
300255
  }
300314
300256
  case "recordSchedule": {
300315
300257
  const resource = resolveResource(trigger.resource, at("resource"), context, fail);
@@ -300319,7 +300261,7 @@ function triggerSettings(trigger, at, context, fail) {
300319
300261
  fail(at("dateField"), '"dateField" must be the key of a date or datetime field');
300320
300262
  return null;
300321
300263
  }
300322
- if (resource !== null) {
300264
+ if (resource !== null && resource.platform !== true) {
300323
300265
  const field2 = resource.fields.find((item) => item.key === dateField);
300324
300266
  if (field2 === void 0 || !SCHEDULE_FIELD_TYPES.has(field2.type)) {
300325
300267
  fail(
@@ -300329,7 +300271,7 @@ function triggerSettings(trigger, at, context, fail) {
300329
300271
  return null;
300330
300272
  }
300331
300273
  }
300332
- return resource === null || offsets === null ? null : { schedule: { offsets }, date_field: dateField, resource_id: resource.id };
300274
+ return resource === null || offsets === null ? null : { schedule: { offsets }, date_field: dateField, resource: resource.slug };
300333
300275
  }
300334
300276
  case "scheduled": {
300335
300277
  const expression2 = trigger.expression;
@@ -300431,11 +300373,11 @@ function readRunAs(runAs, node, trigger, context, report) {
300431
300373
  }
300432
300374
  }
300433
300375
  const resource = resolveResource(runAs.resource, node, context, report);
300434
- if (typeof runAs.record !== "string" || !UUID_PATTERN.test(runAs.record)) {
300376
+ if (typeof runAs.record !== "string" || !UUID_PATTERN2.test(runAs.record)) {
300435
300377
  report(node, '"runAs.record" must be the lowercase UUID of a record');
300436
300378
  return null;
300437
300379
  }
300438
- return resource === null ? null : { mode: "fixed", resource_id: resource.id, record_id: runAs.record };
300380
+ return resource === null ? null : { mode: "fixed", resource: resource.slug, record_id: runAs.record };
300439
300381
  }
300440
300382
  function readAuthorizer(authorizer, node, trigger, report) {
300441
300383
  if (authorizer === void 0 || authorizer === null) {
@@ -300452,24 +300394,97 @@ function readAuthorizer(authorizer, node, trigger, report) {
300452
300394
  return { requires_authentication: true };
300453
300395
  }
300454
300396
 
300455
- // scripts/lib/runtime-build/workflows.mjs
300456
- function resourceLookupKey(value) {
300457
- return value.toLowerCase().replaceAll(/[\s_-]+/g, "");
300397
+ // scripts/lib/runtime-build/workspace-script-types.mjs
300398
+ init_define_globalThis_DIAGONAL_SDK_RUNTIME();
300399
+ var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
300400
+ var CONTENT_TYPE = "content";
300401
+ var NUMBER_TYPES = /* @__PURE__ */ new Set(["number"]);
300402
+ var BOOLEAN_TYPES = /* @__PURE__ */ new Set(["boolean"]);
300403
+ var ARRAY_TYPES = /* @__PURE__ */ new Set(["list"]);
300404
+ var OBJECT_TYPES = /* @__PURE__ */ new Set([
300405
+ "dictionary",
300406
+ "event_schedule",
300407
+ "form",
300408
+ "guard",
300409
+ "identity",
300410
+ "location",
300411
+ "money",
300412
+ "point",
300413
+ "record",
300414
+ "regular_hours",
300415
+ "relation"
300416
+ ]);
300417
+ function fieldTsType(fieldType) {
300418
+ if (fieldType === CONTENT_TYPE) {
300419
+ return "ContentDocument";
300420
+ }
300421
+ if (NUMBER_TYPES.has(fieldType)) {
300422
+ return "number";
300423
+ }
300424
+ if (BOOLEAN_TYPES.has(fieldType)) {
300425
+ return "boolean";
300426
+ }
300427
+ if (ARRAY_TYPES.has(fieldType)) {
300428
+ return "unknown[]";
300429
+ }
300430
+ if (OBJECT_TYPES.has(fieldType)) {
300431
+ return "Record<string, unknown>";
300432
+ }
300433
+ return "string";
300458
300434
  }
300459
- function resourceLookup(resources) {
300460
- const lookup = /* @__PURE__ */ new Map();
300461
- for (const resource of resources) {
300462
- for (const label of [resource.name, resource.singular_name, resource.slug]) {
300463
- const key = resourceLookupKey(label);
300464
- if (!lookup.has(key)) {
300465
- lookup.set(key, resource);
300466
- }
300435
+ function propertyKey2(name) {
300436
+ return IDENTIFIER.test(name) ? name : JSON.stringify(name);
300437
+ }
300438
+ function byteOrder(left, right) {
300439
+ return left < right ? -1 : left > right ? 1 : 0;
300440
+ }
300441
+ function scriptWorkspaceDeclarations(resources, platformResources = []) {
300442
+ const resourcesBySlug = new Map([
300443
+ ...platformResources.map((resource) => [resource.slug, null]),
300444
+ ...resources.map((resource) => [resource.slug, resource])
300445
+ ]);
300446
+ const slugs = [...resourcesBySlug.keys()].toSorted(byteOrder);
300447
+ const types2 = [];
300448
+ let usesContent = false;
300449
+ for (const slug of slugs) {
300450
+ const resource = resourcesBySlug.get(slug);
300451
+ if (resource === null) {
300452
+ types2.push(
300453
+ ` ${propertyKey2(slug)}: { id: '${slug}'; capabilities: string; input: Record<string, unknown>; record: { id: string } & Record<string, unknown> };`
300454
+ );
300455
+ continue;
300456
+ }
300457
+ const input = [];
300458
+ const record = ["id: string"];
300459
+ for (const field2 of resource.fields) {
300460
+ const type = fieldTsType(field2.type);
300461
+ usesContent = usesContent || type === "ContentDocument";
300462
+ const key = propertyKey2(field2.key);
300463
+ input.push(`${key}?: ${type} | null`);
300464
+ record.push(`${key}: ${type} | null`);
300467
300465
  }
300466
+ const capabilities = Object.keys(resource.capabilities ?? {}).toSorted(byteOrder).map((capability) => `'${capability}'`);
300467
+ types2.push(
300468
+ ` ${propertyKey2(slug)}: { id: '${slug}'; capabilities: ${capabilities.length === 0 ? "never" : capabilities.join(" | ")}; input: { ${input.join("; ")} }; record: { ${record.join("; ")} } & Record<string, unknown> };`
300469
+ );
300468
300470
  }
300469
- return lookup;
300471
+ return [
300472
+ "// AUTO-GENERATED for one workspace. Refer to resources with Resources from @app/generated/resources.",
300473
+ ...usesContent ? ["import type { ContentDocument } from '@diagonal/workflow';", ""] : [],
300474
+ "export {};",
300475
+ "",
300476
+ "declare module '@diagonal/workflow' {",
300477
+ " interface WorkspaceResources {",
300478
+ ...types2,
300479
+ " }",
300480
+ "}",
300481
+ ""
300482
+ ].join("\n");
300470
300483
  }
300484
+
300485
+ // scripts/lib/runtime-build/workflows.mjs
300471
300486
  var WORKFLOW_EXTERNALS = /* @__PURE__ */ new Set(["@diagonal/workflow", "@diagonal/workflow/workspace"]);
300472
- function compileScript(path, scriptSource, workspaceDeclarations, values, app) {
300487
+ function compileScript(path, scriptSource, workspaceDeclarations, app) {
300473
300488
  const result = compileWorkflowScript({
300474
300489
  source: scriptSource,
300475
300490
  sdkFiles: SCRIPT_SDK_FILES,
@@ -300489,14 +300504,10 @@ function compileScript(path, scriptSource, workspaceDeclarations, values, app) {
300489
300504
  )
300490
300505
  };
300491
300506
  }
300492
- const names2 = result.resources ?? Object.keys(values);
300493
300507
  return {
300494
300508
  compiled: {
300495
300509
  code: app.imports.length === 0 ? result.code : `${appPrelude(app.modules)}${appRequireShim(app.imports)}${result.code}`,
300496
- input: result.input,
300497
- resources: Object.fromEntries(
300498
- names2.filter((name) => Object.hasOwn(values, name)).map((name) => [name, values[name]])
300499
- )
300510
+ input: result.input
300500
300511
  },
300501
300512
  diagnostics: []
300502
300513
  };
@@ -300520,7 +300531,7 @@ function reportDuplicateTriggers(workflows, diagnostics) {
300520
300531
  if (workflow.type === "request") {
300521
300532
  key = `${workflow.settings.method} ${workflow.settings.route.replaceAll(/\{[^}]+\}/g, "{}")}`;
300522
300533
  } else if (workflow.type === "lifecycle.record") {
300523
- key = `lifecycle ${workflow.settings.resource_id} ${workflow.settings.hook}`;
300534
+ key = `lifecycle ${workflow.settings.resource} ${workflow.settings.hook}`;
300524
300535
  }
300525
300536
  if (key === null) {
300526
300537
  continue;
@@ -300540,28 +300551,28 @@ function reportDuplicateTriggers(workflows, diagnostics) {
300540
300551
  );
300541
300552
  }
300542
300553
  }
300543
- function buildWorkflows({ workflows, tests, resources, resourcesComplete, root = null }) {
300554
+ function buildWorkflows({
300555
+ workflows,
300556
+ tests,
300557
+ resources,
300558
+ platformResources = [],
300559
+ resourcesComplete,
300560
+ names: names2,
300561
+ root = null
300562
+ }) {
300544
300563
  const diagnostics = [];
300545
300564
  if (workflows.length === 0 && tests.length === 0) {
300546
300565
  return { workflows: [], workflowTests: [], diagnostics };
300547
300566
  }
300548
- const { resourcesByName, duplicates } = scriptResourcesByName(resources);
300549
- for (const { name, resource } of duplicates) {
300550
- diagnostics.push(
300551
- diagnosticAt(
300552
- resource.path,
300553
- null,
300554
- `Script resource key "${name}" is shared by multiple resources. Rename one of the resources`,
300555
- { stage: WORKFLOW_STAGE }
300556
- )
300557
- );
300558
- }
300559
- const values = scriptResourceValues(resourcesByName);
300560
- const workspaceDeclarations = scriptWorkspaceDeclarations(resourcesByName);
300561
- const lookup = resourceLookup(resources);
300562
- const resourcesById = new Map(resources.map((resource) => [resource.id, resource]));
300567
+ const workspaceDeclarations = scriptWorkspaceDeclarations(resources, platformResources);
300568
+ const resourcesBySlug = new Map([
300569
+ ...platformResources.map((resource) => [resource.slug, { ...resource, fields: [] }]),
300570
+ ...resources.map((resource) => [resource.slug, resource])
300571
+ ]);
300563
300572
  const context = {
300564
- findResource: (reference) => resourcesById.get(reference) ?? lookup.get(resourceLookupKey(reference)),
300573
+ findResource: (slug) => resourcesBySlug.get(slug),
300574
+ resources,
300575
+ names: names2,
300565
300576
  workflowPaths: new Set(workflows.map((workflow) => workflow.path)),
300566
300577
  reportUnknownResources: resourcesComplete
300567
300578
  };
@@ -300573,9 +300584,17 @@ function buildWorkflows({ workflows, tests, resources, resourcesComplete, root =
300573
300584
  }
300574
300585
  const split = splitWorkflowModule(path, source, ast);
300575
300586
  diagnostics.push(...split.diagnostics);
300576
- const read = split.configNode === null ? { config: null, diagnostics: [] } : readWorkflowConfig(path, split.configNode, context);
300587
+ const read = split.configNode === null ? { config: null, diagnostics: [] } : readWorkflowConfig(path, split.configNode, {
300588
+ ...context,
300589
+ resourcesBinding: resourcesBinding(ast, names2)
300590
+ });
300577
300591
  diagnostics.push(...read.diagnostics);
300578
- const compiled = resourcesComplete ? compileScript(path, split.scriptSource, workspaceDeclarations, values, app) : { compiled: null, diagnostics: [] };
300592
+ if (split.configNode !== null) {
300593
+ diagnostics.push(
300594
+ ...slugStringWarnings(path, split.configNode, names2, { stage: WORKFLOW_STAGE })
300595
+ );
300596
+ }
300597
+ const compiled = resourcesComplete ? compileScript(path, split.scriptSource, workspaceDeclarations, app) : { compiled: null, diagnostics: [] };
300579
300598
  diagnostics.push(...compiled.diagnostics);
300580
300599
  const config = read.config;
300581
300600
  return {
@@ -300591,14 +300610,14 @@ function buildWorkflows({ workflows, tests, resources, resourcesComplete, root =
300591
300610
  script_source: split.scriptSource,
300592
300611
  compiled: compiled.compiled?.code ?? null,
300593
300612
  input: compiled.compiled?.input ?? null,
300594
- script_workspace: compiled.compiled === null ? null : { resources: compiled.compiled.resources }
300613
+ script_workspace: compiled.compiled === null ? null : { resources: {} }
300595
300614
  };
300596
300615
  });
300597
300616
  reportDuplicateTriggers(
300598
300617
  built.filter((workflow) => workflow.type !== null),
300599
300618
  diagnostics
300600
300619
  );
300601
- const testRun = runWorkflowTests({ tests, workflows, resources: values, root, appModules });
300620
+ const testRun = runWorkflowTests({ tests, workflows, root, appModules });
300602
300621
  diagnostics.push(...testRun.diagnostics);
300603
300622
  return { workflows: built, workflowTests: testRun.workflowTests, diagnostics };
300604
300623
  }
@@ -300634,7 +300653,7 @@ function isDirectChild(path, directory) {
300634
300653
  return path.startsWith(`${directory}/`) && !path.slice(directory.length + 1).includes("/");
300635
300654
  }
300636
300655
  function stem(path) {
300637
- return basename2(path, extname(path));
300656
+ return basename3(path, extname2(path));
300638
300657
  }
300639
300658
  function buildConfig(source, ast) {
300640
300659
  const { declaration } = defaultExportOf(ast);
@@ -300680,11 +300699,11 @@ function buildNavigation(path, ast) {
300680
300699
  return { path, id, items };
300681
300700
  }
300682
300701
  function defineResourceArgument(ast) {
300683
- const { declaration } = defaultExportOf(ast);
300702
+ const { declaration } = defaultExportOf(ast, { extraImports: [GENERATED_RESOURCES_MODULE] });
300684
300703
  return expectDefineCall(declaration, "defineResource", 1)[0];
300685
300704
  }
300686
300705
  function isUnitSupportFile(path) {
300687
- const extension = extname(path).toLowerCase();
300706
+ const extension = extname2(path).toLowerCase();
300688
300707
  return extension === ".ts" || extension === ".css" || Object.hasOwn(ASSET_TYPES, extension);
300689
300708
  }
300690
300709
  function classify(path) {
@@ -300692,7 +300711,7 @@ function classify(path) {
300692
300711
  return "config";
300693
300712
  }
300694
300713
  const directory = topDirectory(path);
300695
- const extension = extname(path);
300714
+ const extension = extname2(path);
300696
300715
  if ((directory === "pages" || directory === "layouts" || directory === "components") && isUnitSupportFile(path)) {
300697
300716
  return "ignored";
300698
300717
  }
@@ -300758,32 +300777,81 @@ function reportUnknownComponents(unit, componentsByName, diagnostics) {
300758
300777
  }
300759
300778
  }
300760
300779
  }
300761
- function resolveRelations(resources, lookup, diagnostics) {
300762
- const ids = new Set(resources.map((resource) => resource.id));
300780
+ function checkRelations(resources, platformSlugs, names2, diagnostics) {
300781
+ const slugs = new Set(resources.map((resource) => resource.slug));
300763
300782
  for (const resource of resources) {
300764
- resource.fields = resource.fields.map((field2) => {
300765
- const { relationNode } = field2;
300766
- if (relationNode === void 0) {
300767
- return field2;
300783
+ for (const field2 of resource.fields) {
300784
+ if (field2.relationNode === void 0 || slugs.has(field2.settings.resource)) {
300785
+ continue;
300768
300786
  }
300769
- const target = field2.settings.resource_id;
300770
- if (ids.has(target)) {
300771
- return field2;
300787
+ if (platformSlugs.has(field2.settings.resource)) {
300788
+ const inverse = field2.settings.inverse ?? null;
300789
+ if (inverse !== null && inverse !== false) {
300790
+ diagnostics.push(
300791
+ diagnosticAt(
300792
+ resource.path,
300793
+ field2.relationNode,
300794
+ `Relation "${field2.key}" relates to the platform resource "${field2.settings.resource}", so it cannot add an inverse field to it`
300795
+ )
300796
+ );
300797
+ }
300798
+ continue;
300772
300799
  }
300773
- const match = lookup.get(resourceLookupKey(target));
300774
- if (match === void 0) {
300775
- diagnostics.push(
300776
- diagnosticAt(
300777
- resource.path,
300778
- relationNode,
300779
- `Relation "${field2.key}" refers to unknown resource "${target}"`
300780
- )
300781
- );
300782
- return field2;
300800
+ diagnostics.push(
300801
+ diagnosticAt(
300802
+ resource.path,
300803
+ field2.relationNode,
300804
+ `Relation "${field2.key}": ${referenceProblem(field2.settings.resource, resources, names2).message}`
300805
+ )
300806
+ );
300807
+ }
300808
+ }
300809
+ }
300810
+ function checkPageResources(pages, resources, platformSlugs, names2, diagnostics) {
300811
+ const slugs = /* @__PURE__ */ new Set([...resources.map((resource) => resource.slug), ...platformSlugs]);
300812
+ for (const page of pages) {
300813
+ const references = [
300814
+ ["authorizer.resource", page.authorizer?.resource ?? null, page.resourceNodes.authorizer],
300815
+ ...page.params.map((param) => [
300816
+ `params.${param.key}.resource`,
300817
+ param.resource,
300818
+ page.resourceNodes.params
300819
+ ])
300820
+ ];
300821
+ for (const [option, reference, node] of references) {
300822
+ if (reference === null || slugs.has(reference)) {
300823
+ continue;
300783
300824
  }
300784
- return { ...field2, settings: { ...field2.settings, resource_id: match.id } };
300785
- });
300825
+ diagnostics.push(
300826
+ diagnosticAt(
300827
+ page.path,
300828
+ node,
300829
+ `${option}: ${referenceProblem(reference, resources, names2).message}`
300830
+ )
300831
+ );
300832
+ }
300833
+ }
300834
+ }
300835
+ function syncGeneratedResources(root, names2, write, diagnostics) {
300836
+ const expected = generatedResourcesSource(names2);
300837
+ const path = join9(root, GENERATED_RESOURCES_PATH);
300838
+ const current = existsSync4(path) ? readFileSync7(path, "utf8") : null;
300839
+ if (current === expected || current === null && names2.slugsByName.size === 0) {
300840
+ return;
300841
+ }
300842
+ if (write) {
300843
+ mkdirSync2(dirname6(path), { recursive: true });
300844
+ writeFileSync2(path, expected);
300845
+ return;
300786
300846
  }
300847
+ diagnostics.push(
300848
+ diagnosticAt(
300849
+ GENERATED_RESOURCES_PATH,
300850
+ null,
300851
+ `${current === null ? "The file is missing" : "The file does not match the resources/ folder"}. Run \`diagonal check\` to write it, or write this content:
300852
+ ${expected.trimEnd()}`
300853
+ )
300854
+ );
300787
300855
  }
300788
300856
  function lintUnits(units, resources, components, diagnostics) {
300789
300857
  const catalogs = {
@@ -300797,11 +300865,12 @@ function lintUnits(units, resources, components, diagnostics) {
300797
300865
  function hasErrors(diagnostics) {
300798
300866
  return diagnostics.some((diagnostic2) => diagnostic2.severity === "error");
300799
300867
  }
300800
- function buildWorkspace(root) {
300868
+ function buildWorkspace(root, { writeGeneratedResources = false } = {}) {
300801
300869
  const diagnostics = [];
300802
300870
  const output = {
300803
300871
  config: null,
300804
300872
  resources: [],
300873
+ platform_resources: [],
300805
300874
  pages: [],
300806
300875
  layouts: [],
300807
300876
  components: [],
@@ -300813,8 +300882,14 @@ function buildWorkspace(root) {
300813
300882
  const workflowFiles = [];
300814
300883
  const skillFiles = [];
300815
300884
  const workflowTestFiles = [];
300885
+ const files = listFiles(root);
300886
+ const names2 = resourceNames(
300887
+ files.filter((path) => classify(path) === "resource"),
300888
+ diagnostics
300889
+ );
300890
+ syncGeneratedResources(root, names2, writeGeneratedResources, diagnostics);
300816
300891
  const uiEntries = [];
300817
- for (const path of listFiles(root)) {
300892
+ for (const path of files) {
300818
300893
  const kind = classify(path);
300819
300894
  if (kind === "ignored") {
300820
300895
  continue;
@@ -300831,18 +300906,32 @@ function buildWorkspace(root) {
300831
300906
  continue;
300832
300907
  }
300833
300908
  try {
300834
- const ast = parseSource(source, { jsx: extname(path) === ".tsx" });
300909
+ const ast = parseSource(source, { jsx: extname2(path) === ".tsx" });
300835
300910
  switch (kind) {
300836
300911
  case "config":
300837
300912
  output.config = buildConfig(source, ast);
300838
300913
  break;
300839
- case "resource":
300840
- output.resources.push(buildResource(path, defineResourceArgument(ast)));
300914
+ case "resource": {
300915
+ const slug = stem(path);
300916
+ if (!names2.namesBySlug.has(slug)) {
300917
+ break;
300918
+ }
300919
+ const definition = defineResourceArgument(ast);
300920
+ const resource = buildResource(path, definition, {
300921
+ slug,
300922
+ resources: resourcesBinding(ast, names2)
300923
+ });
300924
+ (resource.platform ? output.platform_resources : output.resources).push(resource);
300925
+ diagnostics.push(...slugStringWarnings(path, definition, names2));
300841
300926
  break;
300842
- case "page":
300843
- output.pages.push(buildPage(path, source, ast, root));
300927
+ }
300928
+ case "page": {
300929
+ const page = buildPage(path, source, ast, root, resourcesBinding(ast, names2));
300930
+ output.pages.push(page);
300931
+ diagnostics.push(...slugStringWarnings(path, page.resourceNodes.meta, names2));
300844
300932
  uiEntries.push({ path, source, ast });
300845
300933
  break;
300934
+ }
300846
300935
  case "layout":
300847
300936
  output.layouts.push(buildLayout(path, stem(path), source, ast, root));
300848
300937
  uiEntries.push({ path, source, ast });
@@ -300868,8 +300957,6 @@ function buildWorkspace(root) {
300868
300957
  if (output.config === null && !diagnostics.some((diagnostic2) => diagnostic2.path === CONFIG_PATH)) {
300869
300958
  diagnostics.push(diagnosticAt(CONFIG_PATH, null, "The workspace has no diagonal.config.ts"));
300870
300959
  }
300871
- reportDuplicates(output.resources, (resource) => resource.id, "resource id", diagnostics);
300872
- reportDuplicates(output.resources, (resource) => resource.slug, "resource slug", diagnostics);
300873
300960
  reportDuplicates(
300874
300961
  output.pages,
300875
300962
  (page) => `${page.domain ?? ""}${normalizeRoute(page.route)}`,
@@ -300886,16 +300973,16 @@ function buildWorkspace(root) {
300886
300973
  "component name",
300887
300974
  diagnostics
300888
300975
  );
300889
- const lookup = resourceLookup(output.resources);
300890
300976
  const resourcesComplete = !diagnostics.some(
300891
- (diagnostic2) => diagnostic2.path.startsWith("resources/")
300977
+ (diagnostic2) => diagnostic2.path.startsWith("resources/") && diagnostic2.severity === "error"
300892
300978
  );
300893
- const referenceDiagnostics = resourcesComplete ? diagnostics : [];
300894
- resolveRelations(output.resources, lookup, referenceDiagnostics);
300979
+ const platformSlugs = new Set(output.platform_resources.map((resource) => resource.slug));
300895
300980
  if (resourcesComplete) {
300981
+ checkRelations(output.resources, platformSlugs, names2, diagnostics);
300896
300982
  addInverseFields(output.resources, diagnostics);
300897
300983
  checkFieldPaths(output.resources, diagnostics);
300898
- checkCapabilities(output.resources, diagnostics);
300984
+ checkCapabilities(output.resources, platformSlugs, diagnostics);
300985
+ checkPageResources(output.pages, output.resources, platformSlugs, names2, diagnostics);
300899
300986
  }
300900
300987
  output.resources = output.resources.map(withoutBuildNodes);
300901
300988
  const packageFiles = readPackageFiles(root, diagnostics);
@@ -300904,7 +300991,9 @@ function buildWorkspace(root) {
300904
300991
  workflows: workflowFiles,
300905
300992
  tests: workflowTestFiles,
300906
300993
  resources: output.resources,
300994
+ platformResources: output.platform_resources,
300907
300995
  resourcesComplete,
300996
+ names: names2,
300908
300997
  root
300909
300998
  });
300910
300999
  diagnostics.push(...workflowBuild.diagnostics);
@@ -300933,24 +301022,26 @@ function buildWorkspace(root) {
300933
301022
  const layouts = output.layouts.map(
300934
301023
  ({ references: _references, lint: _lint, ...layout }) => layout
300935
301024
  );
300936
- const pages = output.pages.map(({ references, layoutNode, lint: _lint, ...page }) => {
300937
- const names2 = references.map((reference) => reference.name);
300938
- if (page.layout !== null) {
300939
- const layout = layoutsByName.get(page.layout);
300940
- if (layout === void 0) {
300941
- diagnostics.push(
300942
- diagnosticAt(
300943
- page.path,
300944
- layoutNode,
300945
- `Unknown layout "${page.layout}". Add layouts/${page.layout}.tsx`
300946
- )
300947
- );
300948
- } else {
300949
- names2.push(...layout.references.map((reference) => reference.name));
301025
+ const pages = output.pages.map(
301026
+ ({ references, layoutNode, resourceNodes: _resourceNodes, lint: _lint, ...page }) => {
301027
+ const names3 = references.map((reference) => reference.name);
301028
+ if (page.layout !== null) {
301029
+ const layout = layoutsByName.get(page.layout);
301030
+ if (layout === void 0) {
301031
+ diagnostics.push(
301032
+ diagnosticAt(
301033
+ page.path,
301034
+ layoutNode,
301035
+ `Unknown layout "${page.layout}". Add layouts/${page.layout}.tsx`
301036
+ )
301037
+ );
301038
+ } else {
301039
+ names3.push(...layout.references.map((reference) => reference.name));
301040
+ }
300950
301041
  }
301042
+ return { ...page, components: componentClosure(names3, componentsByName) };
300951
301043
  }
300952
- return { ...page, components: componentClosure(names2, componentsByName) };
300953
- });
301044
+ );
300954
301045
  return {
300955
301046
  ok: !hasErrors(diagnostics),
300956
301047
  diagnostics: diagnostics.toSorted(
@@ -300958,6 +301049,7 @@ function buildWorkspace(root) {
300958
301049
  ),
300959
301050
  config: output.config,
300960
301051
  resources: output.resources,
301052
+ platform_resources: output.platform_resources,
300961
301053
  pages,
300962
301054
  layouts,
300963
301055
  components,
@@ -301017,7 +301109,7 @@ ${CHECK_USAGE}
301017
301109
  `);
301018
301110
  return 2;
301019
301111
  }
301020
- const result = buildWorkspace(root);
301112
+ const result = buildWorkspace(root, { writeGeneratedResources: true });
301021
301113
  if (parsed.json) {
301022
301114
  stdout.write(
301023
301115
  `${JSON.stringify({ ok: result.ok, diagnostics: result.diagnostics }, null, 2)}