@anthusai/papyrus 1.0.0-next.34 → 1.0.0-next.36

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.
@@ -1290,6 +1290,11 @@ export const schema = a.schema({
1290
1290
  displayName: a.string(),
1291
1291
  description: a.string(),
1292
1292
  aliases: a.string().array(),
1293
+ authorityScore: a.float(),
1294
+ authorityRank: a.integer(),
1295
+ acceptedReferenceMentionCount: a.integer(),
1296
+ distinctSourceKindCount: a.integer(),
1297
+ relationCount: a.integer(),
1293
1298
  status: a.string().required(),
1294
1299
  importRunId: a.id(),
1295
1300
  createdAt: a.datetime(),
@@ -1379,6 +1379,11 @@ export const schema = a.schema({
1379
1379
  displayName: a.string(),
1380
1380
  description: a.string(),
1381
1381
  aliases: a.string().array(),
1382
+ authorityScore: a.float(),
1383
+ authorityRank: a.integer(),
1384
+ acceptedReferenceMentionCount: a.integer(),
1385
+ distinctSourceKindCount: a.integer(),
1386
+ relationCount: a.integer(),
1382
1387
  status: a.string().required(),
1383
1388
  importRunId: a.id(),
1384
1389
  createdAt: a.datetime(),
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { Token } from "aws-cdk-lib";
2
3
  export function sanitizeAwsName(value, maxLength = 50) {
3
4
  return value
4
5
  .toLowerCase()
@@ -7,6 +8,18 @@ export function sanitizeAwsName(value, maxLength = 50) {
7
8
  .replace(/^-|-$/g, "")
8
9
  .slice(0, maxLength);
9
10
  }
11
+ /**
12
+ * Account-global names are immutable in AWS, so they must be built from literal
13
+ * synth-time inputs only. A CDK token (or text that came from one, such as
14
+ * `token-token-17565`) makes the name change on every synth and fail with
15
+ * AlreadyExists, so this throws instead.
16
+ */
17
+ export function assertLiteralResourceName(label, name) {
18
+ if (Token.isUnresolved(name) || /token|\$\{/i.test(name)) {
19
+ throw new Error(`${label} "${name}" is derived from an unresolved CDK token. Build account-global names from literal site config values only.`);
20
+ }
21
+ return name;
22
+ }
10
23
  export function readBooleanEnvFlag(environment, name, defaultValue) {
11
24
  const raw = (environment[name] ?? "").trim().toLowerCase();
12
25
  if (["1", "true", "yes", "on"].includes(raw))
@@ -36,19 +49,21 @@ export function resolveSiteBackendFeatureFlags(features, environment, identity)
36
49
  };
37
50
  }
38
51
  export function deriveKnowledgeVectorIndexName(identity) {
39
- return isLegacyProductionApp(identity)
52
+ const name = isLegacyProductionApp(identity)
40
53
  ? "papyrus-knowledge"
41
54
  : `papyrus-knowledge-${sanitizeAwsName(identity.brandId, 40)}`;
55
+ return assertLiteralResourceName("Knowledge vector index name", name);
42
56
  }
43
- export function deriveStorageBackupVaultName(identity, backupsStackName, explicitName) {
57
+ export function deriveStorageBackupVaultName(identity, explicitName) {
44
58
  const explicit = (explicitName ?? "").trim();
45
59
  if (explicit !== "")
46
- return sanitizeAwsName(explicit);
60
+ return assertLiteralResourceName("Backup vault name", sanitizeAwsName(explicit));
47
61
  if (isLegacyProductionPipeline(identity)) {
48
- return sanitizeAwsName(`papyrus-${identity.productionAppId}-main-media-backup-vault`);
62
+ return assertLiteralResourceName("Backup vault name", sanitizeAwsName(`papyrus-${identity.productionAppId}-main-media-backup-vault`));
49
63
  }
50
- const stackHash = createHash("sha256").update(backupsStackName).digest("hex").slice(0, 8);
51
- return `papyrus-${sanitizeAwsName(identity.brandId, 20)}-${stackHash}-media-vault`;
64
+ const literalSiteKey = [identity.brandId, identity.amplifyAppId, identity.amplifyBranch].join("|");
65
+ const siteHash = createHash("sha256").update(literalSiteKey).digest("hex").slice(0, 8);
66
+ return assertLiteralResourceName("Backup vault name", `papyrus-${sanitizeAwsName(identity.brandId, 20)}-${siteHash}-media-vault`);
52
67
  }
53
68
  /**
54
69
  * The `papyrus-site-inbound-` prefix keeps a brand named `p-apyr-us` from
@@ -57,16 +72,16 @@ export function deriveStorageBackupVaultName(identity, backupsStackName, explici
57
72
  * its own brand-named rule set and never activates it from CloudFormation.
58
73
  */
59
74
  export function deriveReceiptRuleSetName(identity, inboundEmailDomain) {
60
- if (isLegacyProductionApp(identity)) {
61
- return `papyrus-inbound-${inboundEmailDomain.replace(/\./g, "-")}`;
62
- }
63
- return `papyrus-site-inbound-${sanitizeAwsName(identity.brandId, 40)}`;
75
+ const name = isLegacyProductionApp(identity)
76
+ ? `papyrus-inbound-${inboundEmailDomain.replace(/\./g, "-")}`
77
+ : `papyrus-site-inbound-${sanitizeAwsName(identity.brandId, 40)}`;
78
+ return assertLiteralResourceName("Receipt rule set name", name);
64
79
  }
65
80
  /** `undefined` keeps the legacy auto-generated rule name of the live p.apyr.us rule. */
66
81
  export function deriveReceiptRuleName(identity) {
67
82
  if (isLegacyProductionApp(identity))
68
83
  return undefined;
69
- return `${sanitizeAwsName(identity.brandId, 40)}-inbound-submissions`;
84
+ return assertLiteralResourceName("Receipt rule name", `${sanitizeAwsName(identity.brandId, 40)}-inbound-submissions`);
70
85
  }
71
86
  export function planInboundEmailSes(identity, inboundEmailEnabled, options) {
72
87
  const legacyPipeline = isLegacyProductionPipeline(identity);
@@ -78,10 +93,10 @@ export function planInboundEmailSes(identity, inboundEmailEnabled, options) {
78
93
  manageDomainIdentity: createReceiptRules && (legacyPipeline || options?.manageDomainIdentity === true),
79
94
  };
80
95
  }
81
- export function deriveSiteGlobalResourceNames(identity, inboundEmailDomain, backupsStackName) {
96
+ export function deriveSiteGlobalResourceNames(identity, inboundEmailDomain) {
82
97
  return {
83
98
  knowledgeVectorIndexName: deriveKnowledgeVectorIndexName(identity),
84
- storageBackupVaultName: deriveStorageBackupVaultName(identity, backupsStackName),
99
+ storageBackupVaultName: deriveStorageBackupVaultName(identity),
85
100
  receiptRuleSetName: deriveReceiptRuleSetName(identity, inboundEmailDomain),
86
101
  receiptRuleName: deriveReceiptRuleName(identity),
87
102
  };
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { Token } from "aws-cdk-lib";
2
3
 
3
4
  export type SiteBackendFeatureFlags = {
4
5
  consoleResponder: boolean;
@@ -18,6 +19,21 @@ export function sanitizeAwsName(value: string, maxLength = 50): string {
18
19
  .slice(0, maxLength);
19
20
  }
20
21
 
22
+ /**
23
+ * Account-global names are immutable in AWS, so they must be built from literal
24
+ * synth-time inputs only. A CDK token (or text that came from one, such as
25
+ * `token-token-17565`) makes the name change on every synth and fail with
26
+ * AlreadyExists, so this throws instead.
27
+ */
28
+ export function assertLiteralResourceName(label: string, name: string): string {
29
+ if (Token.isUnresolved(name) || /token|\$\{/i.test(name)) {
30
+ throw new Error(
31
+ `${label} "${name}" is derived from an unresolved CDK token. Build account-global names from literal site config values only.`,
32
+ );
33
+ }
34
+ return name;
35
+ }
36
+
21
37
  export function readBooleanEnvFlag(
22
38
  environment: Record<string, string | undefined>,
23
39
  name: string,
@@ -67,23 +83,27 @@ export function resolveSiteBackendFeatureFlags(
67
83
  }
68
84
 
69
85
  export function deriveKnowledgeVectorIndexName(identity: SiteBackendIdentity): string {
70
- return isLegacyProductionApp(identity)
86
+ const name = isLegacyProductionApp(identity)
71
87
  ? "papyrus-knowledge"
72
88
  : `papyrus-knowledge-${sanitizeAwsName(identity.brandId, 40)}`;
89
+ return assertLiteralResourceName("Knowledge vector index name", name);
73
90
  }
74
91
 
75
- export function deriveStorageBackupVaultName(
76
- identity: SiteBackendIdentity,
77
- backupsStackName: string,
78
- explicitName?: string,
79
- ): string {
92
+ export function deriveStorageBackupVaultName(identity: SiteBackendIdentity, explicitName?: string): string {
80
93
  const explicit = (explicitName ?? "").trim();
81
- if (explicit !== "") return sanitizeAwsName(explicit);
94
+ if (explicit !== "") return assertLiteralResourceName("Backup vault name", sanitizeAwsName(explicit));
82
95
  if (isLegacyProductionPipeline(identity)) {
83
- return sanitizeAwsName(`papyrus-${identity.productionAppId}-main-media-backup-vault`);
96
+ return assertLiteralResourceName(
97
+ "Backup vault name",
98
+ sanitizeAwsName(`papyrus-${identity.productionAppId}-main-media-backup-vault`),
99
+ );
84
100
  }
85
- const stackHash = createHash("sha256").update(backupsStackName).digest("hex").slice(0, 8);
86
- return `papyrus-${sanitizeAwsName(identity.brandId, 20)}-${stackHash}-media-vault`;
101
+ const literalSiteKey = [identity.brandId, identity.amplifyAppId, identity.amplifyBranch].join("|");
102
+ const siteHash = createHash("sha256").update(literalSiteKey).digest("hex").slice(0, 8);
103
+ return assertLiteralResourceName(
104
+ "Backup vault name",
105
+ `papyrus-${sanitizeAwsName(identity.brandId, 20)}-${siteHash}-media-vault`,
106
+ );
87
107
  }
88
108
 
89
109
  /**
@@ -93,16 +113,16 @@ export function deriveStorageBackupVaultName(
93
113
  * its own brand-named rule set and never activates it from CloudFormation.
94
114
  */
95
115
  export function deriveReceiptRuleSetName(identity: SiteBackendIdentity, inboundEmailDomain: string): string {
96
- if (isLegacyProductionApp(identity)) {
97
- return `papyrus-inbound-${inboundEmailDomain.replace(/\./g, "-")}`;
98
- }
99
- return `papyrus-site-inbound-${sanitizeAwsName(identity.brandId, 40)}`;
116
+ const name = isLegacyProductionApp(identity)
117
+ ? `papyrus-inbound-${inboundEmailDomain.replace(/\./g, "-")}`
118
+ : `papyrus-site-inbound-${sanitizeAwsName(identity.brandId, 40)}`;
119
+ return assertLiteralResourceName("Receipt rule set name", name);
100
120
  }
101
121
 
102
122
  /** `undefined` keeps the legacy auto-generated rule name of the live p.apyr.us rule. */
103
123
  export function deriveReceiptRuleName(identity: SiteBackendIdentity): string | undefined {
104
124
  if (isLegacyProductionApp(identity)) return undefined;
105
- return `${sanitizeAwsName(identity.brandId, 40)}-inbound-submissions`;
125
+ return assertLiteralResourceName("Receipt rule name", `${sanitizeAwsName(identity.brandId, 40)}-inbound-submissions`);
106
126
  }
107
127
 
108
128
  export type InboundEmailSesPlan = {
@@ -136,11 +156,10 @@ export type SiteGlobalResourceNames = {
136
156
  export function deriveSiteGlobalResourceNames(
137
157
  identity: SiteBackendIdentity,
138
158
  inboundEmailDomain: string,
139
- backupsStackName: string,
140
159
  ): SiteGlobalResourceNames {
141
160
  return {
142
161
  knowledgeVectorIndexName: deriveKnowledgeVectorIndexName(identity),
143
- storageBackupVaultName: deriveStorageBackupVaultName(identity, backupsStackName),
162
+ storageBackupVaultName: deriveStorageBackupVaultName(identity),
144
163
  receiptRuleSetName: deriveReceiptRuleSetName(identity, inboundEmailDomain),
145
164
  receiptRuleName: deriveReceiptRuleName(identity),
146
165
  };
@@ -220,7 +220,7 @@ export function defineSiteBackend(papyrusSite) {
220
220
  }
221
221
  if (enableStorageBackups) {
222
222
  const storageBackupsStack = backend.createStack("storage-backups");
223
- const storageBackupVaultName = deriveStorageBackupVaultName(siteBackendIdentity, storageBackupsStack.stackName, process.env.PAPYRUS_STORAGE_BACKUP_VAULT_NAME);
223
+ const storageBackupVaultName = deriveStorageBackupVaultName(siteBackendIdentity, process.env.PAPYRUS_STORAGE_BACKUP_VAULT_NAME);
224
224
  const storageBackups = addStorageBackups(storageBackupsStack, {
225
225
  storageBucket,
226
226
  backupVaultName: storageBackupVaultName,
@@ -282,7 +282,6 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
282
282
  const storageBackupsStack = backend.createStack("storage-backups");
283
283
  const storageBackupVaultName = deriveStorageBackupVaultName(
284
284
  siteBackendIdentity,
285
- storageBackupsStack.stackName,
286
285
  process.env.PAPYRUS_STORAGE_BACKUP_VAULT_NAME,
287
286
  );
288
287
 
package/package.json CHANGED
@@ -300,7 +300,7 @@
300
300
  "optional": true
301
301
  }
302
302
  },
303
- "version": "1.0.0-next.34",
303
+ "version": "1.0.0-next.36",
304
304
  "dependencies": {
305
305
  "@aws-amplify/adapter-nextjs": "^1.7.3",
306
306
  "@aws-sdk/client-cognito-identity-provider": "3.1045.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.0-next.34",
2
+ "version": "1.0.0-next.36",
3
3
  "routes": [
4
4
  {
5
5
  "file": "app/%5F_preview/[[...path]]/route.ts",
@@ -9,6 +9,7 @@ import { fileURLToPath } from "node:url";
9
9
  * `papyrus-amplify-outputs` -> ./amplify_outputs.json and
10
10
  * `papyrus-site-theme` -> ./publication/theme.css when it exists, else the shipped
11
11
  * empty lib/empty-theme.css (both webpack and turbopack),
12
+ * - trace the corpora/ YAML and amplify_outputs.json that the reader reads via fs at runtime,
12
13
  * - carry Papyrus's own server-external packages and image settings.
13
14
  */
14
15
  const SERVER_EXTERNAL = [
@@ -22,6 +23,15 @@ const SERVER_EXTERNAL = [
22
23
  "@aws-amplify/data-schema",
23
24
  ];
24
25
 
26
+ export const RUNTIME_TRACED_FILES = ["./corpora/**/*", "./amplify_outputs.json"];
27
+
28
+ export function mergeOutputFileTracingIncludes(existing = {}) {
29
+ const merged = { ...existing };
30
+ const current = merged["/**"] ?? [];
31
+ merged["/**"] = [...new Set([...current, ...RUNTIME_TRACED_FILES])];
32
+ return merged;
33
+ }
34
+
25
35
  const EMPTY_THEME = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../lib/empty-theme.css");
26
36
 
27
37
  export function withPapyrus(nextConfig = {}, options = {}) {
@@ -41,6 +51,7 @@ export function withPapyrus(nextConfig = {}, options = {}) {
41
51
  ...nextConfig,
42
52
  transpilePackages: [...new Set([...(nextConfig.transpilePackages ?? []), "@anthusai/papyrus"])],
43
53
  serverExternalPackages: [...new Set([...(nextConfig.serverExternalPackages ?? []), ...SERVER_EXTERNAL])],
54
+ outputFileTracingIncludes: mergeOutputFileTracingIncludes(nextConfig.outputFileTracingIncludes),
44
55
  images: nextConfig.images ?? { remotePatterns: [{ protocol: "https", hostname: "**" }] },
45
56
  experimental: { devtoolSegmentExplorer: false, ...nextConfig.experimental },
46
57
  turbopack: {