@things-factory/figure-service 10.1.159 → 10.1.161

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.
@@ -0,0 +1,10 @@
1
+ export declare class FigureNamespace {
2
+ namespace?: string;
3
+ written: string;
4
+ subdomain: string;
5
+ fixed: boolean;
6
+ }
7
+ export declare class FigureNamespaceResolver {
8
+ figureNamespace(context: ResolverContext): Promise<FigureNamespace>;
9
+ setFigureNamespace(namespace: string, context: ResolverContext): Promise<FigureNamespace>;
10
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FigureNamespaceResolver = exports.FigureNamespace = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
7
+ const figure_namespace_js_1 = require("./figure-namespace.js");
8
+ let FigureNamespace = class FigureNamespace {
9
+ };
10
+ exports.FigureNamespace = FigureNamespace;
11
+ tslib_1.__decorate([
12
+ (0, type_graphql_1.Field)({ nullable: true, description: 'The namespace new figures are created under; null when neither the setting nor the subdomain is one.' }),
13
+ tslib_1.__metadata("design:type", String)
14
+ ], FigureNamespace.prototype, "namespace", void 0);
15
+ tslib_1.__decorate([
16
+ (0, type_graphql_1.Field)({ description: 'The figure-namespace setting as written; empty when the subdomain is used.' }),
17
+ tslib_1.__metadata("design:type", String)
18
+ ], FigureNamespace.prototype, "written", void 0);
19
+ tslib_1.__decorate([
20
+ (0, type_graphql_1.Field)({ description: "The domain's subdomain, used when the setting is empty." }),
21
+ tslib_1.__metadata("design:type", String)
22
+ ], FigureNamespace.prototype, "subdomain", void 0);
23
+ tslib_1.__decorate([
24
+ (0, type_graphql_1.Field)({ description: "True once the domain's figures carry the namespace: it cannot be set any more." }),
25
+ tslib_1.__metadata("design:type", Boolean)
26
+ ], FigureNamespace.prototype, "fixed", void 0);
27
+ exports.FigureNamespace = FigureNamespace = tslib_1.__decorate([
28
+ (0, type_graphql_1.ObjectType)({ description: "A domain's figure namespace, as the setting screen shows it (ADR-0114)." })
29
+ ], FigureNamespace);
30
+ let FigureNamespaceResolver = class FigureNamespaceResolver {
31
+ async figureNamespace(context) {
32
+ const { domain, tx } = context.state;
33
+ return (await (0, figure_namespace_js_1.figureNamespaceStateOf)(tx ?? (0, shell_1.getDataSource)().manager, domain));
34
+ }
35
+ async setFigureNamespace(namespace, context) {
36
+ const { domain, tx } = context.state;
37
+ return (await (0, figure_namespace_js_1.setFigureNamespace)(tx, domain, namespace));
38
+ }
39
+ };
40
+ exports.FigureNamespaceResolver = FigureNamespaceResolver;
41
+ tslib_1.__decorate([
42
+ (0, type_graphql_1.Directive)('@privilege(category: "figure", privilege: "query", domainOwnerGranted: true)'),
43
+ (0, type_graphql_1.Query)(returns => FigureNamespace, { description: "The domain's figure namespace and whether it can still be set." }),
44
+ tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
45
+ tslib_1.__metadata("design:type", Function),
46
+ tslib_1.__metadata("design:paramtypes", [Object]),
47
+ tslib_1.__metadata("design:returntype", Promise)
48
+ ], FigureNamespaceResolver.prototype, "figureNamespace", null);
49
+ tslib_1.__decorate([
50
+ (0, type_graphql_1.Directive)('@transaction'),
51
+ (0, type_graphql_1.Directive)('@privilege(category: "setting", privilege: "mutation", domainOwnerGranted: true)'),
52
+ (0, type_graphql_1.Mutation)(returns => FigureNamespace, {
53
+ description: "Sets the domain's figure namespace. Empty takes the subdomain. Refused when it is not a namespace, or when the domain's figures already carry another."
54
+ }),
55
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('namespace')),
56
+ tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
57
+ tslib_1.__metadata("design:type", Function),
58
+ tslib_1.__metadata("design:paramtypes", [String, Object]),
59
+ tslib_1.__metadata("design:returntype", Promise)
60
+ ], FigureNamespaceResolver.prototype, "setFigureNamespace", null);
61
+ exports.FigureNamespaceResolver = FigureNamespaceResolver = tslib_1.__decorate([
62
+ (0, type_graphql_1.Resolver)(),
63
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
64
+ ], FigureNamespaceResolver);
65
+ //# sourceMappingURL=figure-namespace-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-namespace-resolver.js","sourceRoot":"","sources":["../../../server/service/figure/figure-namespace-resolver.ts"],"names":[],"mappings":";;;;AAAA,+CAA+G;AAE/G,iDAAoE;AAEpE,+DAAkF;AAG3E,IAAM,eAAe,GAArB,MAAM,eAAe;CAY3B,CAAA;AAZY,0CAAe;AAE1B;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sGAAsG,EAAE,CAAC;;kDAC7H;AAGlB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,4EAA4E,EAAE,CAAC;;gDACtF;AAGf;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;;kDACjE;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,gFAAgF,EAAE,CAAC;;8CAC3F;0BAXH,eAAe;IAD3B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,yEAAyE,EAAE,CAAC;GAC1F,eAAe,CAY3B;AAIM,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAG5B,AAAN,KAAK,CAAC,eAAe,CAAQ,OAAwB;QACnD,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,OAAO,CAAC,MAAM,IAAA,4CAAsB,EAAC,EAAE,IAAI,IAAA,qBAAa,GAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAoB,CAAA;IACjG,CAAC;IAOK,AAAN,KAAK,CAAC,kBAAkB,CAAmB,SAAiB,EAAS,OAAwB;QAC3F,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,OAAO,CAAC,MAAM,IAAA,wCAAkB,EAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,CAAoB,CAAA;IAC7E,CAAC;CACF,CAAA;AAjBY,0DAAuB;AAG5B;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,eAAe,EAAE,EAAE,WAAW,EAAE,gEAAgE,EAAE,CAAC;IAC9F,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;8DAG3B;AAOK;IALL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,kFAAkF,CAAC;IAC7F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,eAAe,EAAE;QACpC,WAAW,EAAE,wJAAwJ;KACtK,CAAC;IACwB,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iEAGnE;kCAhBU,uBAAuB;IAFnC,IAAA,uBAAQ,GAAE;IACV,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,uBAAuB,CAiBnC","sourcesContent":["import { Arg, Ctx, Directive, Field, Mutation, ObjectType, Query, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { getDataSource, SpeakRefusals } from '@things-factory/shell'\n\nimport { figureNamespaceStateOf, setFigureNamespace } from './figure-namespace.js'\n\n@ObjectType({ description: \"A domain's figure namespace, as the setting screen shows it (ADR-0114).\" })\nexport class FigureNamespace {\n @Field({ nullable: true, description: 'The namespace new figures are created under; null when neither the setting nor the subdomain is one.' })\n namespace?: string\n\n @Field({ description: 'The figure-namespace setting as written; empty when the subdomain is used.' })\n written: string\n\n @Field({ description: \"The domain's subdomain, used when the setting is empty.\" })\n subdomain: string\n\n @Field({ description: \"True once the domain's figures carry the namespace: it cannot be set any more.\" })\n fixed: boolean\n}\n\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class FigureNamespaceResolver {\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureNamespace, { description: \"The domain's figure namespace and whether it can still be set.\" })\n async figureNamespace(@Ctx() context: ResolverContext): Promise<FigureNamespace> {\n const { domain, tx } = context.state\n return (await figureNamespaceStateOf(tx ?? getDataSource().manager, domain)) as FigureNamespace\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"setting\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => FigureNamespace, {\n description: \"Sets the domain's figure namespace. Empty takes the subdomain. Refused when it is not a namespace, or when the domain's figures already carry another.\"\n })\n async setFigureNamespace(@Arg('namespace') namespace: string, @Ctx() context: ResolverContext): Promise<FigureNamespace> {\n const { domain, tx } = context.state\n return (await setFigureNamespace(tx, domain, namespace)) as FigureNamespace\n }\n}\n"]}
@@ -17,3 +17,19 @@ export declare function namespaceForNewFigure(tx: EntityManager, domain: Domain)
17
17
  export declare function assertNamespaceUnchanged(tx: EntityManager, domain: Domain, namespace: string): Promise<void>;
18
18
  /** The namespace a figure already carries, refused when it has none (a figure made before namespaces). */
19
19
  export declare function namespaceOf(figure: Figure): string;
20
+ /** A domain's figure namespace as the setting screen shows it. */
21
+ export interface FigureNamespaceState {
22
+ /** What new figures get, or null when neither the setting nor the subdomain is a namespace. */
23
+ namespace: string | null;
24
+ /** The setting as written; empty when the subdomain is used. */
25
+ written: string;
26
+ subdomain: string;
27
+ /** The domain's figures already carry the namespace, so it cannot be set any more. */
28
+ fixed: boolean;
29
+ }
30
+ export declare function figureNamespaceStateOf(tx: EntityManager, domain: Domain): Promise<FigureNamespaceState>;
31
+ /**
32
+ * Writes the domain's figure namespace (ADR-0114). Empty takes the subdomain. Refused when the value, or the subdomain
33
+ * it falls back to, is not a namespace, and when the domain's figures already carry another one.
34
+ */
35
+ export declare function setFigureNamespace(tx: EntityManager, domain: Domain, value: string): Promise<FigureNamespaceState>;
@@ -4,6 +4,8 @@ exports.FIGURE_NAMESPACE_SETTING = void 0;
4
4
  exports.namespaceForNewFigure = namespaceForNewFigure;
5
5
  exports.assertNamespaceUnchanged = assertNamespaceUnchanged;
6
6
  exports.namespaceOf = namespaceOf;
7
+ exports.figureNamespaceStateOf = figureNamespaceStateOf;
8
+ exports.setFigureNamespace = setFigureNamespace;
7
9
  const typeorm_1 = require("typeorm");
8
10
  const figure_model_1 = require("@hatiolab/figure-model");
9
11
  const shell_1 = require("@things-factory/shell");
@@ -21,11 +23,7 @@ exports.FIGURE_NAMESPACE_SETTING = 'figure-namespace';
21
23
  * from the namespace this domain's figures already carry is refused, so editing the setting cannot rename anything.
22
24
  */
23
25
  async function namespaceForNewFigure(tx, domain) {
24
- const setting = await tx.getRepository(setting_base_1.Setting).findOne({
25
- where: { domain: { id: domain.id }, name: exports.FIGURE_NAMESPACE_SETTING }
26
- });
27
- const written = setting?.value?.trim() ?? '';
28
- const namespace = written || (domain.subdomain ?? '').trim();
26
+ const namespace = await chosenNamespace(tx, domain);
29
27
  if (!(0, figure_model_1.isFigureNamespace)(namespace)) {
30
28
  throw new shell_1.Refusal('figure-namespace-missing', { setting: exports.FIGURE_NAMESPACE_SETTING, value: namespace }, 'this domain has no valid figure namespace. set {setting} first, for example hatiolab.com.');
31
29
  }
@@ -48,4 +46,42 @@ function namespaceOf(figure) {
48
46
  }
49
47
  return figure.namespace;
50
48
  }
49
+ /** What the setting says, or the subdomain when it says nothing: the namespace new figures are created under. */
50
+ async function chosenNamespace(tx, domain) {
51
+ return (await writtenNamespace(tx, domain)) || (domain.subdomain ?? '').trim();
52
+ }
53
+ async function writtenNamespace(tx, domain) {
54
+ const setting = await tx.getRepository(setting_base_1.Setting).findOne({
55
+ where: { domain: { id: domain.id }, name: exports.FIGURE_NAMESPACE_SETTING }
56
+ });
57
+ return setting?.value?.trim() ?? '';
58
+ }
59
+ async function figureNamespaceStateOf(tx, domain) {
60
+ const written = await writtenNamespace(tx, domain);
61
+ const subdomain = (domain.subdomain ?? '').trim();
62
+ const carried = await tx.getRepository(figure_js_1.Figure).findOne({ where: { domain: { id: domain.id }, namespace: (0, typeorm_1.Not)((0, typeorm_1.IsNull)()) } });
63
+ const chosen = written || subdomain;
64
+ return {
65
+ namespace: carried?.namespace ?? ((0, figure_model_1.isFigureNamespace)(chosen) ? chosen : null),
66
+ written,
67
+ subdomain,
68
+ fixed: !!carried?.namespace
69
+ };
70
+ }
71
+ /**
72
+ * Writes the domain's figure namespace (ADR-0114). Empty takes the subdomain. Refused when the value, or the subdomain
73
+ * it falls back to, is not a namespace, and when the domain's figures already carry another one.
74
+ */
75
+ async function setFigureNamespace(tx, domain, value) {
76
+ const written = value.trim();
77
+ const namespace = written || (domain.subdomain ?? '').trim();
78
+ if (!(0, figure_model_1.isFigureNamespace)(namespace)) {
79
+ throw new shell_1.Refusal('figure-namespace-invalid', { value: namespace }, '{value} is not a figure namespace. use lower-case letters, digits, dots and hyphens, for example hatiolab.com.');
80
+ }
81
+ await assertNamespaceUnchanged(tx, domain, namespace);
82
+ const repository = tx.getRepository(setting_base_1.Setting);
83
+ const setting = await repository.findOne({ where: { domain: { id: domain.id }, name: exports.FIGURE_NAMESPACE_SETTING } });
84
+ await repository.save({ ...setting, domain, name: exports.FIGURE_NAMESPACE_SETTING, category: 'figure', value: written });
85
+ return figureNamespaceStateOf(tx, domain);
86
+ }
51
87
  //# sourceMappingURL=figure-namespace.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"figure-namespace.js","sourceRoot":"","sources":["../../../server/service/figure/figure-namespace.ts"],"names":[],"mappings":";;;AAoBA,sDAeC;AAGD,4DAWC;AAGD,kCASC;AA7DD,qCAA4C;AAE5C,yDAA0D;AAC1D,iDAAuD;AACvD,+DAAsD;AAEtD,2CAAoC;AAEpC,wGAAwG;AAC3F,QAAA,wBAAwB,GAAG,kBAAkB,CAAA;AAE1D;;;;;;;;GAQG;AACI,KAAK,UAAU,qBAAqB,CAAC,EAAiB,EAAE,MAAc;IAC3E,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,sBAAO,CAAC,CAAC,OAAO,CAAC;QACtD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,gCAAwB,EAAE;KACrE,CAAC,CAAA;IACF,MAAM,OAAO,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAC5C,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC5D,IAAI,CAAC,IAAA,gCAAiB,EAAC,SAAS,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,eAAO,CACf,0BAA0B,EAC1B,EAAE,OAAO,EAAE,gCAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,EACvD,2FAA2F,CAC5F,CAAA;IACH,CAAC;IACD,MAAM,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;IACrD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,mGAAmG;AAC5F,KAAK,UAAU,wBAAwB,CAAC,EAAiB,EAAE,MAAc,EAAE,SAAiB;IACjG,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,kBAAM,CAAC,CAAC,OAAO,CAAC;QACnD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAA,aAAG,EAAC,SAAS,CAAC,EAAE;KAChE,CAAC,CAAA;IACF,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC;QACrB,MAAM,IAAI,eAAO,CACf,0BAA0B,EAC1B,EAAE,OAAO,EAAE,gCAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,EACnF,0GAA0G,CAC3G,CAAA;IACH,CAAC;AACH,CAAC;AAED,0GAA0G;AAC1G,SAAgB,WAAW,CAAC,MAAc;IACxC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,eAAO,CACf,0BAA0B,EAC1B,EAAE,OAAO,EAAE,gCAAwB,EAAE,KAAK,EAAE,EAAE,EAAE,EAChD,oEAAoE,CACrE,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAC,SAAS,CAAA;AACzB,CAAC","sourcesContent":["import { EntityManager, Not } from 'typeorm'\n\nimport { isFigureNamespace } from '@hatiolab/figure-model'\nimport { Domain, Refusal } from '@things-factory/shell'\nimport { Setting } from '@things-factory/setting-base'\n\nimport { Figure } from './figure.js'\n\n/** The domain setting that names the namespace this domain's figures are published under (ADR-0114). */\nexport const FIGURE_NAMESPACE_SETTING = 'figure-namespace'\n\n/**\n * The namespace a new figure is created under.\n *\n * It is read from the domain setting and copied onto the figure, so the full name `{namespace}/{type}` a board stores\n * never moves. A domain with no setting, or an empty one, takes its subdomain (user, 2026-10-13: 「기본적으로는\n * subdomain 명을 사용하면 되는데」) -- refused only when the subdomain is not a namespace either. A value written in\n * the setting is used as it is, and refused when it is not a namespace. Either way it is set once: a value that differs\n * from the namespace this domain's figures already carry is refused, so editing the setting cannot rename anything.\n */\nexport async function namespaceForNewFigure(tx: EntityManager, domain: Domain): Promise<string> {\n const setting = await tx.getRepository(Setting).findOne({\n where: { domain: { id: domain.id }, name: FIGURE_NAMESPACE_SETTING }\n })\n const written = setting?.value?.trim() ?? ''\n const namespace = written || (domain.subdomain ?? '').trim()\n if (!isFigureNamespace(namespace)) {\n throw new Refusal(\n 'figure-namespace-missing',\n { setting: FIGURE_NAMESPACE_SETTING, value: namespace },\n 'this domain has no valid figure namespace. set {setting} first, for example hatiolab.com.'\n )\n }\n await assertNamespaceUnchanged(tx, domain, namespace)\n return namespace\n}\n\n/** Refuses when the domain's figures already carry another namespace than the setting now says. */\nexport async function assertNamespaceUnchanged(tx: EntityManager, domain: Domain, namespace: string): Promise<void> {\n const other = await tx.getRepository(Figure).findOne({\n where: { domain: { id: domain.id }, namespace: Not(namespace) }\n })\n if (other?.namespace) {\n throw new Refusal(\n 'figure-namespace-changed',\n { setting: FIGURE_NAMESPACE_SETTING, value: namespace, namespace: other.namespace },\n 'the figure namespace of this domain is {namespace} and cannot change. set {setting} back to {namespace}.'\n )\n }\n}\n\n/** The namespace a figure already carries, refused when it has none (a figure made before namespaces). */\nexport function namespaceOf(figure: Figure): string {\n if (!figure.namespace) {\n throw new Refusal(\n 'figure-namespace-missing',\n { setting: FIGURE_NAMESPACE_SETTING, value: '' },\n 'this figure has no namespace. run the figure name migration first.'\n )\n }\n return figure.namespace\n}\n"]}
1
+ {"version":3,"file":"figure-namespace.js","sourceRoot":"","sources":["../../../server/service/figure/figure-namespace.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, IsNull, Not } from 'typeorm'\n\nimport { isFigureNamespace } from '@hatiolab/figure-model'\nimport { Domain, Refusal } from '@things-factory/shell'\nimport { Setting } from '@things-factory/setting-base'\n\nimport { Figure } from './figure.js'\n\n/** The domain setting that names the namespace this domain's figures are published under (ADR-0114). */\nexport const FIGURE_NAMESPACE_SETTING = 'figure-namespace'\n\n/**\n * The namespace a new figure is created under.\n *\n * It is read from the domain setting and copied onto the figure, so the full name `{namespace}/{type}` a board stores\n * never moves. A domain with no setting, or an empty one, takes its subdomain (user, 2026-10-13: 「기본적으로는\n * subdomain 명을 사용하면 되는데」) -- refused only when the subdomain is not a namespace either. A value written in\n * the setting is used as it is, and refused when it is not a namespace. Either way it is set once: a value that differs\n * from the namespace this domain's figures already carry is refused, so editing the setting cannot rename anything.\n */\nexport async function namespaceForNewFigure(tx: EntityManager, domain: Domain): Promise<string> {\n const namespace = await chosenNamespace(tx, domain)\n if (!isFigureNamespace(namespace)) {\n throw new Refusal(\n 'figure-namespace-missing',\n { setting: FIGURE_NAMESPACE_SETTING, value: namespace },\n 'this domain has no valid figure namespace. set {setting} first, for example hatiolab.com.'\n )\n }\n await assertNamespaceUnchanged(tx, domain, namespace)\n return namespace\n}\n\n/** Refuses when the domain's figures already carry another namespace than the setting now says. */\nexport async function assertNamespaceUnchanged(tx: EntityManager, domain: Domain, namespace: string): Promise<void> {\n const other = await tx.getRepository(Figure).findOne({\n where: { domain: { id: domain.id }, namespace: Not(namespace) }\n })\n if (other?.namespace) {\n throw new Refusal(\n 'figure-namespace-changed',\n { setting: FIGURE_NAMESPACE_SETTING, value: namespace, namespace: other.namespace },\n 'the figure namespace of this domain is {namespace} and cannot change. set {setting} back to {namespace}.'\n )\n }\n}\n\n/** The namespace a figure already carries, refused when it has none (a figure made before namespaces). */\nexport function namespaceOf(figure: Figure): string {\n if (!figure.namespace) {\n throw new Refusal(\n 'figure-namespace-missing',\n { setting: FIGURE_NAMESPACE_SETTING, value: '' },\n 'this figure has no namespace. run the figure name migration first.'\n )\n }\n return figure.namespace\n}\n\n/** What the setting says, or the subdomain when it says nothing: the namespace new figures are created under. */\nasync function chosenNamespace(tx: EntityManager, domain: Domain): Promise<string> {\n return (await writtenNamespace(tx, domain)) || (domain.subdomain ?? '').trim()\n}\n\nasync function writtenNamespace(tx: EntityManager, domain: Domain): Promise<string> {\n const setting = await tx.getRepository(Setting).findOne({\n where: { domain: { id: domain.id }, name: FIGURE_NAMESPACE_SETTING }\n })\n return setting?.value?.trim() ?? ''\n}\n\n/** A domain's figure namespace as the setting screen shows it. */\nexport interface FigureNamespaceState {\n /** What new figures get, or null when neither the setting nor the subdomain is a namespace. */\n namespace: string | null\n /** The setting as written; empty when the subdomain is used. */\n written: string\n subdomain: string\n /** The domain's figures already carry the namespace, so it cannot be set any more. */\n fixed: boolean\n}\n\nexport async function figureNamespaceStateOf(tx: EntityManager, domain: Domain): Promise<FigureNamespaceState> {\n const written = await writtenNamespace(tx, domain)\n const subdomain = (domain.subdomain ?? '').trim()\n const carried = await tx.getRepository(Figure).findOne({ where: { domain: { id: domain.id }, namespace: Not(IsNull()) } })\n const chosen = written || subdomain\n return {\n namespace: carried?.namespace ?? (isFigureNamespace(chosen) ? chosen : null),\n written,\n subdomain,\n fixed: !!carried?.namespace\n }\n}\n\n/**\n * Writes the domain's figure namespace (ADR-0114). Empty takes the subdomain. Refused when the value, or the subdomain\n * it falls back to, is not a namespace, and when the domain's figures already carry another one.\n */\nexport async function setFigureNamespace(tx: EntityManager, domain: Domain, value: string): Promise<FigureNamespaceState> {\n const written = value.trim()\n const namespace = written || (domain.subdomain ?? '').trim()\n if (!isFigureNamespace(namespace)) {\n throw new Refusal(\n 'figure-namespace-invalid',\n { value: namespace },\n '{value} is not a figure namespace. use lower-case letters, digits, dots and hyphens, for example hatiolab.com.'\n )\n }\n await assertNamespaceUnchanged(tx, domain, namespace)\n const repository = tx.getRepository(Setting)\n const setting = await repository.findOne({ where: { domain: { id: domain.id }, name: FIGURE_NAMESPACE_SETTING } })\n await repository.save({ ...setting, domain, name: FIGURE_NAMESPACE_SETTING, category: 'figure', value: written })\n return figureNamespaceStateOf(tx, domain)\n}\n"]}
@@ -4,7 +4,8 @@ import { FigureReplacedDraft } from './figure-replaced-draft.js';
4
4
  import { FigureProposalRecord } from './figure-proposal-record.js';
5
5
  import { FigureQuery } from './figure-query.js';
6
6
  import { FigureMutation } from './figure-mutation.js';
7
+ import { FigureNamespaceResolver } from './figure-namespace-resolver.js';
7
8
  import { FigureHistoryEntitySubscriber } from './event-subscriber.js';
8
9
  export declare const entities: (typeof Figure | typeof FigureHistory | typeof FigureReplacedDraft | typeof FigureProposalRecord)[];
9
- export declare const resolvers: (typeof FigureQuery | typeof FigureMutation)[];
10
+ export declare const resolvers: (typeof FigureQuery | typeof FigureMutation | typeof FigureNamespaceResolver)[];
10
11
  export declare const subscribers: (typeof FigureHistoryEntitySubscriber)[];
@@ -7,8 +7,9 @@ const figure_replaced_draft_js_1 = require("./figure-replaced-draft.js");
7
7
  const figure_proposal_record_js_1 = require("./figure-proposal-record.js");
8
8
  const figure_query_js_1 = require("./figure-query.js");
9
9
  const figure_mutation_js_1 = require("./figure-mutation.js");
10
+ const figure_namespace_resolver_js_1 = require("./figure-namespace-resolver.js");
10
11
  const event_subscriber_js_1 = require("./event-subscriber.js");
11
12
  exports.entities = [figure_js_1.Figure, figure_history_js_1.FigureHistory, figure_replaced_draft_js_1.FigureReplacedDraft, figure_proposal_record_js_1.FigureProposalRecord];
12
- exports.resolvers = [figure_query_js_1.FigureQuery, figure_mutation_js_1.FigureMutation];
13
+ exports.resolvers = [figure_query_js_1.FigureQuery, figure_mutation_js_1.FigureMutation, figure_namespace_resolver_js_1.FigureNamespaceResolver];
13
14
  exports.subscribers = [event_subscriber_js_1.FigureHistoryEntitySubscriber];
14
15
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/figure/index.ts"],"names":[],"mappings":";;;AAAA,2CAAoC;AACpC,2DAAmD;AACnD,yEAAgE;AAChE,2EAAkE;AAClE,uDAA+C;AAC/C,6DAAqD;AACrD,+DAAqE;AAExD,QAAA,QAAQ,GAAG,CAAC,kBAAM,EAAE,iCAAa,EAAE,8CAAmB,EAAE,gDAAoB,CAAC,CAAA;AAC7E,QAAA,SAAS,GAAG,CAAC,6BAAW,EAAE,mCAAc,CAAC,CAAA;AACzC,QAAA,WAAW,GAAG,CAAC,mDAA6B,CAAC,CAAA","sourcesContent":["import { Figure } from './figure.js'\nimport { FigureHistory } from './figure-history.js'\nimport { FigureReplacedDraft } from './figure-replaced-draft.js'\nimport { FigureProposalRecord } from './figure-proposal-record.js'\nimport { FigureQuery } from './figure-query.js'\nimport { FigureMutation } from './figure-mutation.js'\nimport { FigureHistoryEntitySubscriber } from './event-subscriber.js'\n\nexport const entities = [Figure, FigureHistory, FigureReplacedDraft, FigureProposalRecord]\nexport const resolvers = [FigureQuery, FigureMutation]\nexport const subscribers = [FigureHistoryEntitySubscriber]\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/figure/index.ts"],"names":[],"mappings":";;;AAAA,2CAAoC;AACpC,2DAAmD;AACnD,yEAAgE;AAChE,2EAAkE;AAClE,uDAA+C;AAC/C,6DAAqD;AACrD,iFAAwE;AACxE,+DAAqE;AAExD,QAAA,QAAQ,GAAG,CAAC,kBAAM,EAAE,iCAAa,EAAE,8CAAmB,EAAE,gDAAoB,CAAC,CAAA;AAC7E,QAAA,SAAS,GAAG,CAAC,6BAAW,EAAE,mCAAc,EAAE,sDAAuB,CAAC,CAAA;AAClE,QAAA,WAAW,GAAG,CAAC,mDAA6B,CAAC,CAAA","sourcesContent":["import { Figure } from './figure.js'\nimport { FigureHistory } from './figure-history.js'\nimport { FigureReplacedDraft } from './figure-replaced-draft.js'\nimport { FigureProposalRecord } from './figure-proposal-record.js'\nimport { FigureQuery } from './figure-query.js'\nimport { FigureMutation } from './figure-mutation.js'\nimport { FigureNamespaceResolver } from './figure-namespace-resolver.js'\nimport { FigureHistoryEntitySubscriber } from './event-subscriber.js'\n\nexport const entities = [Figure, FigureHistory, FigureReplacedDraft, FigureProposalRecord]\nexport const resolvers = [FigureQuery, FigureMutation, FigureNamespaceResolver]\nexport const subscribers = [FigureHistoryEntitySubscriber]\n"]}
@@ -7,5 +7,5 @@ export { recipeTemplates, inLanguage, RECIPE_DIR } from './figure-recipe/figure-
7
7
  export declare const entities: (typeof import("./figure/figure.js").Figure | typeof import("./figure/figure-history.js").FigureHistory | typeof import("./figure/figure-replaced-draft.js").FigureReplacedDraft | typeof import("./figure/figure-proposal-record.js").FigureProposalRecord)[];
8
8
  export declare const subscribers: typeof import("./figure/event-subscriber.js").FigureHistoryEntitySubscriber[];
9
9
  export declare const schema: {
10
- resolverClasses: (typeof import("./figure/figure-query.js").FigureQuery | typeof import("./figure/figure-mutation.js").FigureMutation | typeof import("./figure-recipe/figure-recipe-resolver.js").FigureRecipeTemplateQuery)[];
10
+ resolverClasses: (typeof import("./figure/figure-query.js").FigureQuery | typeof import("./figure/figure-mutation.js").FigureMutation | typeof import("./figure/figure-namespace-resolver.js").FigureNamespaceResolver | typeof import("./figure-recipe/figure-recipe-resolver.js").FigureRecipeTemplateQuery)[];
11
11
  };