@formigio/fazemos-cli 0.6.1 → 0.7.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/api.d.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import { type AuthMeResponse } from './config.js';
1
2
  /**
2
3
  * Error thrown when an API request returns a non-2xx response. Carries the
3
4
  * `code` field from the API's error envelope (when present) so command
4
- * handlers can branch on specific failure modes (e.g., EMAIL_SEND_FAILED).
5
+ * handlers can branch on specific failure modes (e.g., EMAIL_SEND_FAILED,
6
+ * F15's MISSING_PROJECT_CONTEXT / PROJECT_NOT_FOUND / PROJECT_MISMATCH).
5
7
  * Also exposes the parsed body so callers can read extra fields like
6
8
  * `inviteEmail`/`sessionEmail` on INVITE_EMAIL_MISMATCH.
7
9
  */
@@ -11,4 +13,63 @@ export declare class ApiError extends Error {
11
13
  body?: unknown;
12
14
  constructor(message: string, status: number, code?: string, body?: unknown);
13
15
  }
14
- export declare function api(method: string, path: string, body?: unknown): Promise<unknown>;
16
+ /**
17
+ * F15 — options for `api()` that controls project context threading.
18
+ *
19
+ * - `projectSlug` — override active project for this call. The CLI resolves
20
+ * slug → id from the cached /auth/me response, falling back to a single
21
+ * refresh + retry on miss (per §8.3 slug-miss cache refresh behavior).
22
+ * - `allProjects` — send `?view=all` query param on GET list endpoints. Set
23
+ * by `--all-projects` on any scoped list command (§8.2 of the tech spec).
24
+ * - `noProjectHeader` — for endpoints that are not project-scoped (e.g.,
25
+ * /auth/me itself, /api/organizations/mine). Skip header emission so the
26
+ * API doesn't misinterpret a stale projectId.
27
+ */
28
+ export interface ApiOptions {
29
+ projectSlug?: string;
30
+ allProjects?: boolean;
31
+ noProjectHeader?: boolean;
32
+ }
33
+ /**
34
+ * Resolve the project id to send as X-Fazemos-Project-Id on a scoped call.
35
+ *
36
+ * Resolution order:
37
+ * 1. If a slug override is provided (--project <slug>), look it up in the
38
+ * cached /auth/me response. On a cache miss, refresh once (covers the
39
+ * "just created on another host" case) and retry.
40
+ * 2. Otherwise, return the active project id from config.
41
+ *
42
+ * On a slug that refuses to resolve after refresh → throws ApiError with
43
+ * code UNKNOWN_PROJECT. The outer command handler prints the standard
44
+ * "unknown project: <slug>" and exits 1.
45
+ */
46
+ export declare function resolveProjectIdBySlug(slugOverride?: string): Promise<string | null>;
47
+ /**
48
+ * Fetch /auth/me fresh from the API and cache the response. Used on slug
49
+ * miss and by any command that has just mutated orgs/projects (create,
50
+ * archive) and needs subsequent lookups to see the new data.
51
+ */
52
+ export declare function refreshAuthMeCache(): Promise<AuthMeResponse | null>;
53
+ /**
54
+ * Core API helper. Always authenticates, always sends X-Org-Id when one is
55
+ * active, and sends X-Fazemos-Project-Id when a project is active (or one
56
+ * has been resolved via `projectSlug` override).
57
+ *
58
+ * Error handling (F15): if the API returns `code: 'MISSING_PROJECT_CONTEXT'`
59
+ * we re-shape the thrown ApiError to the uniform KD9 shape — `message` is
60
+ * the literal string `requirement missing: project`, `code` is preserved.
61
+ * This keeps the CLI's "missing required input" surface uniform and lets
62
+ * outer handlers print the standard hint block without substring-matching
63
+ * API message text.
64
+ *
65
+ * Other codes (PROJECT_NOT_FOUND, PROJECT_MISMATCH, VALIDATION_ERROR) pass
66
+ * through verbatim so the caller sees the API's precise message. This is
67
+ * the "thin validator" of KD9: translate one specific code, pass the rest.
68
+ */
69
+ export declare function api(method: string, path: string, body?: unknown, opts?: ApiOptions): Promise<unknown>;
70
+ /**
71
+ * Invalidate the /auth/me cache after any mutation that changes the set of
72
+ * projects (create/archive/unarchive) or orgs. Called from command
73
+ * handlers to keep the next slug-resolution honest.
74
+ */
75
+ export declare function invalidateAuthMeCache(): void;
package/dist/api.js CHANGED
@@ -1,9 +1,10 @@
1
- import { getEnv, getToken, getActiveOrgId } from './config.js';
1
+ import { getEnv, getToken, getActiveOrgId, getActiveProjectId, findProjectBySlug, setAuthMeCache, clearAuthMeCache, } from './config.js';
2
2
  import { refreshSession } from './auth.js';
3
3
  /**
4
4
  * Error thrown when an API request returns a non-2xx response. Carries the
5
5
  * `code` field from the API's error envelope (when present) so command
6
- * handlers can branch on specific failure modes (e.g., EMAIL_SEND_FAILED).
6
+ * handlers can branch on specific failure modes (e.g., EMAIL_SEND_FAILED,
7
+ * F15's MISSING_PROJECT_CONTEXT / PROJECT_NOT_FOUND / PROJECT_MISMATCH).
7
8
  * Also exposes the parsed body so callers can read extra fields like
8
9
  * `inviteEmail`/`sessionEmail` on INVITE_EMAIL_MISMATCH.
9
10
  */
@@ -19,7 +20,76 @@ export class ApiError extends Error {
19
20
  this.body = body;
20
21
  }
21
22
  }
22
- export async function api(method, path, body) {
23
+ /**
24
+ * Resolve the project id to send as X-Fazemos-Project-Id on a scoped call.
25
+ *
26
+ * Resolution order:
27
+ * 1. If a slug override is provided (--project <slug>), look it up in the
28
+ * cached /auth/me response. On a cache miss, refresh once (covers the
29
+ * "just created on another host" case) and retry.
30
+ * 2. Otherwise, return the active project id from config.
31
+ *
32
+ * On a slug that refuses to resolve after refresh → throws ApiError with
33
+ * code UNKNOWN_PROJECT. The outer command handler prints the standard
34
+ * "unknown project: <slug>" and exits 1.
35
+ */
36
+ export async function resolveProjectIdBySlug(slugOverride) {
37
+ const orgId = getActiveOrgId();
38
+ if (!orgId)
39
+ return null;
40
+ if (slugOverride) {
41
+ let project = findProjectBySlug(orgId, slugOverride);
42
+ if (!project) {
43
+ // Force-refresh the /auth/me cache. A slug miss is cheap to verify
44
+ // and catches the common "created via another host or via the web
45
+ // UI and not yet cached here" path.
46
+ await refreshAuthMeCache();
47
+ project = findProjectBySlug(orgId, slugOverride);
48
+ }
49
+ if (!project) {
50
+ throw new ApiError(`unknown project: ${slugOverride}`, 400, 'UNKNOWN_PROJECT');
51
+ }
52
+ return project.id;
53
+ }
54
+ return getActiveProjectId();
55
+ }
56
+ /**
57
+ * Fetch /auth/me fresh from the API and cache the response. Used on slug
58
+ * miss and by any command that has just mutated orgs/projects (create,
59
+ * archive) and needs subsequent lookups to see the new data.
60
+ */
61
+ export async function refreshAuthMeCache() {
62
+ try {
63
+ const me = (await api('GET', '/auth/me', undefined, { noProjectHeader: true }));
64
+ if (me && typeof me === 'object' && 'orgs' in me) {
65
+ setAuthMeCache(me);
66
+ return me;
67
+ }
68
+ }
69
+ catch {
70
+ // Swallow — caller will surface any follow-up error naturally. We
71
+ // don't want a /auth/me refresh failure to mask the user's real
72
+ // command error.
73
+ }
74
+ return null;
75
+ }
76
+ /**
77
+ * Core API helper. Always authenticates, always sends X-Org-Id when one is
78
+ * active, and sends X-Fazemos-Project-Id when a project is active (or one
79
+ * has been resolved via `projectSlug` override).
80
+ *
81
+ * Error handling (F15): if the API returns `code: 'MISSING_PROJECT_CONTEXT'`
82
+ * we re-shape the thrown ApiError to the uniform KD9 shape — `message` is
83
+ * the literal string `requirement missing: project`, `code` is preserved.
84
+ * This keeps the CLI's "missing required input" surface uniform and lets
85
+ * outer handlers print the standard hint block without substring-matching
86
+ * API message text.
87
+ *
88
+ * Other codes (PROJECT_NOT_FOUND, PROJECT_MISMATCH, VALIDATION_ERROR) pass
89
+ * through verbatim so the caller sees the API's precise message. This is
90
+ * the "thin validator" of KD9: translate one specific code, pass the rest.
91
+ */
92
+ export async function api(method, path, body, opts = {}) {
23
93
  const env = getEnv();
24
94
  let token = getToken();
25
95
  const orgId = getActiveOrgId();
@@ -39,7 +109,25 @@ export async function api(method, path, body) {
39
109
  if (orgId) {
40
110
  headers['X-Org-Id'] = orgId;
41
111
  }
42
- const url = `${env.apiUrl}${path}`;
112
+ // F15 project context header. Skipped for non-project-scoped endpoints
113
+ // (auth/me, /api/organizations/mine, org CRUD). The API ignores the header
114
+ // on endpoints that don't require it, but omitting it keeps intent clear
115
+ // in request logs.
116
+ if (!opts.noProjectHeader) {
117
+ const projectId = await resolveProjectIdBySlug(opts.projectSlug);
118
+ if (projectId) {
119
+ headers['X-Fazemos-Project-Id'] = projectId;
120
+ }
121
+ }
122
+ // Build URL with optional ?view=all append. We don't try to be clever
123
+ // about merging with existing query strings here — callers that use
124
+ // --all-projects know they're on GET list endpoints with no other query.
125
+ // If a future endpoint wants both --all-projects and a manual query,
126
+ // extend this helper rather than working around it.
127
+ let url = `${env.apiUrl}${path}`;
128
+ if (opts.allProjects) {
129
+ url += (url.includes('?') ? '&' : '?') + 'view=all';
130
+ }
43
131
  const options = {
44
132
  method,
45
133
  headers,
@@ -60,9 +148,23 @@ export async function api(method, path, body) {
60
148
  const obj = typeof data === 'object' && data !== null
61
149
  ? data
62
150
  : null;
151
+ const code = obj?.code;
63
152
  const msg = obj?.error ?? `HTTP ${response.status}`;
64
- throw new ApiError(msg, response.status, obj?.code, data);
153
+ // KD9 translate MISSING_PROJECT_CONTEXT into the uniform CLI
154
+ // missing-input shape. Other F15 codes pass through verbatim.
155
+ if (code === 'MISSING_PROJECT_CONTEXT') {
156
+ throw new ApiError('requirement missing: project', response.status, code, data);
157
+ }
158
+ throw new ApiError(msg, response.status, code, data);
65
159
  }
66
160
  return data;
67
161
  }
162
+ /**
163
+ * Invalidate the /auth/me cache after any mutation that changes the set of
164
+ * projects (create/archive/unarchive) or orgs. Called from command
165
+ * handlers to keep the next slug-resolution honest.
166
+ */
167
+ export function invalidateAuthMeCache() {
168
+ clearAuthMeCache();
169
+ }
68
170
  //# sourceMappingURL=api.js.map
package/dist/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,MAAM,CAAS;IACf,IAAI,CAAU;IACd,IAAI,CAAW;IACf,YAAY,OAAe,EAAE,MAAc,EAAE,IAAa,EAAE,IAAc;QACxE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,MAAc,EAAE,IAAY,EAAE,IAAc;IACpE,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAE/B,mCAAmC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,MAAM,cAAc,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;KACnC,CAAC;IACF,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC;IAC/C,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;IAC9B,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;IACnC,MAAM,OAAO,GAAgB;QAC3B,MAAM;QACN,OAAO;KACR,CAAC;IAEF,IAAI,IAAI,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAEnC,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,GAAG,GACP,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;YACvC,CAAC,CAAE,IAA0C;YAC7C,CAAC,CAAC,IAAI,CAAC;QACX,MAAM,GAAG,GAAG,GAAG,EAAE,KAAK,IAAI,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpD,MAAM,IAAI,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -1,4 +1,47 @@
1
1
  import Conf from 'conf';
2
+ /**
3
+ * Shape of a project as cached in /auth/me response.
4
+ */
5
+ export interface CachedProject {
6
+ id: string;
7
+ slug: string;
8
+ name: string;
9
+ colorIndex: number;
10
+ status: 'active' | 'archived';
11
+ }
12
+ /**
13
+ * Shape of an org membership as cached in /auth/me response.
14
+ */
15
+ export interface CachedOrg {
16
+ id: string;
17
+ slug: string;
18
+ name: string;
19
+ role: 'owner' | 'admin' | 'member';
20
+ memberId: string;
21
+ projects: CachedProject[];
22
+ }
23
+ /**
24
+ * Cached /auth/me response. Used to resolve project slug → id without a
25
+ * network round-trip on every scoped command (§8.4 of the F15 tech spec).
26
+ */
27
+ export interface AuthMeResponse {
28
+ user: {
29
+ id: string;
30
+ email: string;
31
+ };
32
+ member: {
33
+ id: string;
34
+ orgId: string;
35
+ displayName: string;
36
+ email: string | null;
37
+ role: string;
38
+ memberType: string;
39
+ status: string;
40
+ };
41
+ orgs: CachedOrg[];
42
+ activeOrgId: string;
43
+ activeProjectId: string | null;
44
+ }
2
45
  interface FazemosConfig {
3
46
  environments: Record<string, {
4
47
  apiUrl: string;
@@ -8,13 +51,31 @@ interface FazemosConfig {
8
51
  }>;
9
52
  activeEnv: string;
10
53
  activeOrgId: string;
54
+ /**
55
+ * F15 — Active project per org. When the user switches orgs, their last
56
+ * active project for the destination org is restored from this map (per
57
+ * UX §1.8). Keyed by orgId so a single machine can track multiple orgs'
58
+ * project selections independently.
59
+ */
60
+ activeProjectByOrg: Record<string, string>;
11
61
  auth: Record<string, {
12
62
  email: string;
13
63
  idToken: string;
14
64
  refreshToken: string;
15
65
  expiresAt: number;
16
66
  }>;
67
+ /**
68
+ * F15 — Cached /auth/me response. 5-minute TTL. Invalidated explicitly
69
+ * on any mutating projects/orgs command (create, archive) and refreshed
70
+ * lazily on first use by resolveProjectId() when a slug miss happens.
71
+ */
72
+ authMeCache?: {
73
+ activeEnv: string;
74
+ fetchedAt: number;
75
+ data: AuthMeResponse;
76
+ };
17
77
  }
78
+ export declare const AUTH_ME_CACHE_TTL_MS: number;
18
79
  export declare const config: Conf<FazemosConfig>;
19
80
  export declare function addEnvironment(name: string, env: {
20
81
  apiUrl: string;
@@ -34,4 +95,33 @@ export declare function getToken(): string | null;
34
95
  export declare function getActiveOrgId(): string | null;
35
96
  export declare function setActiveOrgId(orgId: string): void;
36
97
  export declare function setAuth(env: string, email: string, idToken: string, refreshToken: string, expiresInSeconds: number): void;
98
+ /**
99
+ * Returns the active project id for the currently-active org, or null if
100
+ * none is set. Follows KD9 — no auto-pick, no interactive prompt. The
101
+ * scoped-command handlers translate a null return into the uniform
102
+ * "requirement missing: project" error when the API says so.
103
+ */
104
+ export declare function getActiveProjectId(): string | null;
105
+ /**
106
+ * Sets the active project for the given org. Stored as a per-org map so
107
+ * switching orgs restores the last project you used in each.
108
+ */
109
+ export declare function setActiveProjectId(orgId: string, projectId: string): void;
110
+ /**
111
+ * Clears the active project for the given org (e.g., when the project
112
+ * is archived and was the active one).
113
+ */
114
+ export declare function clearActiveProjectId(orgId: string): void;
115
+ export declare function getAuthMeCache(): AuthMeResponse | null;
116
+ export declare function setAuthMeCache(data: AuthMeResponse): void;
117
+ export declare function clearAuthMeCache(): void;
118
+ /**
119
+ * Look up a project by slug in the currently cached /auth/me response.
120
+ * Returns null if the cache is empty, stale, or the slug is not present.
121
+ * Callers should refresh the cache once and retry on miss — see
122
+ * resolveProjectIdBySlug in api.ts.
123
+ */
124
+ export declare function findProjectBySlug(orgId: string, slug: string): CachedProject | null;
125
+ export declare function findProjectById(orgId: string, projectId: string): CachedProject | null;
126
+ export declare function findOrgById(orgId: string): CachedOrg | null;
37
127
  export {};
package/dist/config.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import Conf from 'conf';
2
+ export const AUTH_ME_CACHE_TTL_MS = 5 * 60 * 1000;
2
3
  export const config = new Conf({
3
4
  projectName: 'fazemos-cli',
4
5
  defaults: {
5
6
  environments: {},
6
7
  activeEnv: '',
7
8
  activeOrgId: '',
9
+ activeProjectByOrg: {},
8
10
  auth: {},
9
11
  },
10
12
  });
@@ -53,4 +55,92 @@ export function setAuth(env, email, idToken, refreshToken, expiresInSeconds) {
53
55
  };
54
56
  config.set('auth', auth);
55
57
  }
58
+ // ── F15 — Project context helpers ───────────────────────────
59
+ /**
60
+ * Returns the active project id for the currently-active org, or null if
61
+ * none is set. Follows KD9 — no auto-pick, no interactive prompt. The
62
+ * scoped-command handlers translate a null return into the uniform
63
+ * "requirement missing: project" error when the API says so.
64
+ */
65
+ export function getActiveProjectId() {
66
+ const orgId = getActiveOrgId();
67
+ if (!orgId)
68
+ return null;
69
+ const map = config.get('activeProjectByOrg') ?? {};
70
+ return map[orgId] ?? null;
71
+ }
72
+ /**
73
+ * Sets the active project for the given org. Stored as a per-org map so
74
+ * switching orgs restores the last project you used in each.
75
+ */
76
+ export function setActiveProjectId(orgId, projectId) {
77
+ const map = config.get('activeProjectByOrg') ?? {};
78
+ map[orgId] = projectId;
79
+ config.set('activeProjectByOrg', map);
80
+ }
81
+ /**
82
+ * Clears the active project for the given org (e.g., when the project
83
+ * is archived and was the active one).
84
+ */
85
+ export function clearActiveProjectId(orgId) {
86
+ const map = config.get('activeProjectByOrg') ?? {};
87
+ delete map[orgId];
88
+ config.set('activeProjectByOrg', map);
89
+ }
90
+ // ── F15 — /auth/me cache helpers ────────────────────────────
91
+ export function getAuthMeCache() {
92
+ const cache = config.get('authMeCache');
93
+ if (!cache)
94
+ return null;
95
+ const activeEnv = config.get('activeEnv');
96
+ if (cache.activeEnv !== activeEnv)
97
+ return null;
98
+ if (Date.now() - cache.fetchedAt > AUTH_ME_CACHE_TTL_MS)
99
+ return null;
100
+ return cache.data;
101
+ }
102
+ export function setAuthMeCache(data) {
103
+ const activeEnv = config.get('activeEnv');
104
+ config.set('authMeCache', {
105
+ activeEnv,
106
+ fetchedAt: Date.now(),
107
+ data,
108
+ });
109
+ }
110
+ export function clearAuthMeCache() {
111
+ // conf's .delete strips the key entirely; using set(undefined) keeps the
112
+ // field present but null which confuses narrowing callers. Delete is the
113
+ // cleaner surface here.
114
+ config.delete('authMeCache');
115
+ }
116
+ /**
117
+ * Look up a project by slug in the currently cached /auth/me response.
118
+ * Returns null if the cache is empty, stale, or the slug is not present.
119
+ * Callers should refresh the cache once and retry on miss — see
120
+ * resolveProjectIdBySlug in api.ts.
121
+ */
122
+ export function findProjectBySlug(orgId, slug) {
123
+ const me = getAuthMeCache();
124
+ if (!me)
125
+ return null;
126
+ const org = me.orgs.find(o => o.id === orgId);
127
+ if (!org)
128
+ return null;
129
+ return org.projects.find(p => p.slug === slug) ?? null;
130
+ }
131
+ export function findProjectById(orgId, projectId) {
132
+ const me = getAuthMeCache();
133
+ if (!me)
134
+ return null;
135
+ const org = me.orgs.find(o => o.id === orgId);
136
+ if (!org)
137
+ return null;
138
+ return org.projects.find(p => p.id === projectId) ?? null;
139
+ }
140
+ export function findOrgById(orgId) {
141
+ const me = getAuthMeCache();
142
+ if (!me)
143
+ return null;
144
+ return me.orgs.find(o => o.id === orgId) ?? null;
145
+ }
56
146
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAmBxB,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,IAAI,CAAgB;IAC5C,WAAW,EAAE,aAAa;IAC1B,QAAQ,EAAE;QACR,YAAY,EAAE,EAAE;QAChB,SAAS,EAAE,EAAE;QACb,WAAW,EAAE,EAAE;QACf,IAAI,EAAE,EAAE;KACT;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,GAA8F;IACzI,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;IACjB,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IACjC,iCAAiC;IACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,kBAAkB,CAAC,CAAC;IAClE,OAAO,EAAE,IAAI,EAAE,GAAG,GAAG,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,IAAI,CAAC,OAAO,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACxC,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAW,EAAE,KAAa,EAAE,OAAe,EAAE,YAAoB,EAAE,gBAAwB;IACjH,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,GAAG,CAAC,GAAG;QACV,KAAK;QACL,OAAO;QACP,YAAY;QACZ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,GAAG,IAAI;KAChD,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}