@cyc-seattle/platform 1.35.0 → 1.37.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/README.md +7 -7
- package/dist/outputs.d.ts +2 -0
- package/dist/outputs.js +4 -0
- package/dist/planning/activities.js +1 -1
- package/dist/planning/context.d.ts +6 -1
- package/dist/planning/context.js +21 -3
- package/dist/planning/engine.js +1 -1
- package/dist/planning/staffing.d.ts +27 -27
- package/dist/planning/staffing.js +353 -344
- package/dist/planning/types.d.ts +54 -39
- package/dist/schema/index.d.ts +70 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@ app owns. The apps that run on it live in their own repos, such as
|
|
|
6
6
|
|
|
7
7
|
## Stacks
|
|
8
8
|
|
|
9
|
-
| Stack | Holds
|
|
10
|
-
| ------------------------------- |
|
|
11
|
-
| [`bootstrap`](stacks/bootstrap) | Service accounts and project IAM, the Pulumi state buckets and KMS keys,
|
|
12
|
-
| [`infra`](stacks/infra) | The VPC, the cluster subnet and its Cloud NAT on a static address, the private services peering, the prod and dev Cloud SQL instances, the DNS zones, Artifact Registry and its cleanup policy, the GKE Autopilot cluster, the shared Gateway, its address, certificates, and dev's DNS records, the `dev` and `prod` namespaces with their quotas and network policies, dev's RBAC, the prod Job alerts, the log exclusions, and each environment's `platform/outputs.json` | `deploy-runner`, from Actions after approval. A deployer, with `mise run deploy`, when breaking glass. |
|
|
13
|
-
| [`smoke`](stacks/smoke) | One `WebService` in dev, at `smoke.dev.cycsail.team`, for the integration tests
|
|
14
|
-
| [`backend`](stacks/backend) | Directus, with the schema apply from `schemas/`, its roles, policies, and permission rules, the sync machine users, its OIDC client, and MCP, and the clubspot-sync and gsuite-sync CronJobs, with dev's Clubspot and Workspace fakes
|
|
15
|
-
| [`authentik`](stacks/authentik) | Authentik, the sign-in at `login.<domain>`: its server and worker, database, secrets, and A record, dev's Mailpit, and its own configuration: flows, stages, policies, the Google source, the `staff` group, and the Directus OIDC and portal proxy applications
|
|
9
|
+
| Stack | Holds | Applied by |
|
|
10
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
|
|
11
|
+
| [`bootstrap`](stacks/bootstrap) | Service accounts and project IAM, the Pulumi state buckets and KMS keys, the org's GitHub repos and settings, and Firebase on the project | A human with project Owner and `gh` `admin:org`. Never CI. |
|
|
12
|
+
| [`infra`](stacks/infra) | The VPC, the cluster subnet and its Cloud NAT on a static address, the private services peering, the prod and dev Cloud SQL instances, the DNS zones, Artifact Registry and its cleanup policy, the GKE Autopilot cluster, the shared Gateway, its address, certificates, and dev's DNS records, the `dev` and `prod` namespaces with their quotas and network policies, dev's RBAC, the prod Job alerts, the log exclusions, the public website's Firebase Hosting site, and each environment's `platform/outputs.json` | `deploy-runner`, from Actions after approval. A deployer, with `mise run deploy`, when breaking glass. |
|
|
13
|
+
| [`smoke`](stacks/smoke) | One `WebService` in dev, at `smoke.dev.cycsail.team`, for the integration tests | `deploy-dev`, from Actions for each queued PR. A deployer, with `PULUMI_STACK=dev mise run deploy`, when breaking glass. |
|
|
14
|
+
| [`backend`](stacks/backend) | Directus, with the schema apply from `schemas/`, its roles, policies, and permission rules, the sync machine users, its OIDC client, and MCP, and the clubspot-sync and gsuite-sync CronJobs, with dev's Clubspot and Workspace fakes | `deploy-dev`, from Actions for each queued PR, and `deploy-runner` on a push to `main`, after `infra`. |
|
|
15
|
+
| [`authentik`](stacks/authentik) | Authentik, the sign-in at `login.<domain>`: its server and worker, database, secrets, and A record, dev's Mailpit, and its own configuration: flows, stages, policies, the Google source, the `staff` group, and the Directus OIDC and portal proxy applications | `deploy-dev`, from Actions for each queued PR, and `deploy-runner` on a push to `main`, after `backend`. |
|
|
16
16
|
|
|
17
17
|
## @cyc-seattle/platform
|
|
18
18
|
|
package/dist/outputs.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export type PlatformOutputs = z.infer<typeof platformOutputsSchema>;
|
|
|
22
22
|
export declare const outputsObject = "platform/outputs.json";
|
|
23
23
|
/** The state bucket of an environment, as `stacks/bootstrap/state.ts` names it. */
|
|
24
24
|
export declare function stateBucket(project: string, environment: string): string;
|
|
25
|
+
/** The bucket Directus keeps an environment's uploaded files in, as `stacks/infra/directus-files.ts` names it. */
|
|
26
|
+
export declare function directusFilesBucket(project: string, environment: string): string;
|
|
25
27
|
/** Parses and validates the outputs file's text. `source` names the file in error messages. */
|
|
26
28
|
export declare function parsePlatformOutputs(text: string, source: string): PlatformOutputs;
|
|
27
29
|
export interface PlatformOutputsArgs {
|
package/dist/outputs.js
CHANGED
|
@@ -24,6 +24,10 @@ export const outputsObject = "platform/outputs.json";
|
|
|
24
24
|
export function stateBucket(project, environment) {
|
|
25
25
|
return `${project}-pulumi-${environment}`;
|
|
26
26
|
}
|
|
27
|
+
/** The bucket Directus keeps an environment's uploaded files in, as `stacks/infra/directus-files.ts` names it. */
|
|
28
|
+
export function directusFilesBucket(project, environment) {
|
|
29
|
+
return `${project}-directus-files-${environment}`;
|
|
30
|
+
}
|
|
27
31
|
/** Parses and validates the outputs file's text. `source` names the file in error messages. */
|
|
28
32
|
export function parsePlatformOutputs(text, source) {
|
|
29
33
|
let json;
|
|
@@ -32,7 +32,7 @@ export function scheduleActivities(ctx) {
|
|
|
32
32
|
const span = start !== null && end !== null ? end - start : 0;
|
|
33
33
|
const closedFor = closed.get(activity.date);
|
|
34
34
|
if (closedFor !== undefined && activity.kind !== "travel") {
|
|
35
|
-
ctx.warn("closed_date", `${offering.name} has an activity on ${activity.date}, a closed day: ${closedFor}`, activity.key);
|
|
35
|
+
ctx.warn("closed_date", `${offering.name} has an activity on ${activity.date}, a closed day: ${closedFor}`, activity.key, { version_id: activity.version_id, date: activity.date });
|
|
36
36
|
}
|
|
37
37
|
const usesBoats = activity.kind !== "travel" && activity.uses_boats;
|
|
38
38
|
result.push({
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { Account, AccountFunction, CostCenterType, CostType, DateString, EngineWarning, Id, PlanData, PlanOffering, Plan, PlanRates, Product, ProgramArea } from "./types.ts";
|
|
2
|
+
/** Where a warning applies: a plan copy, and a date. */
|
|
3
|
+
export interface WarningScope {
|
|
4
|
+
version_id?: Id;
|
|
5
|
+
date?: DateString;
|
|
6
|
+
}
|
|
2
7
|
/** The step that recorded an entry. Only `direct` entries count toward MTDC. */
|
|
3
8
|
export type Stage = "direct" | "pool" | "bonus";
|
|
4
9
|
/** One amount, in fractional cents, before lines are rounded and summed. */
|
|
@@ -45,7 +50,7 @@ export declare class Context {
|
|
|
45
50
|
constructor(data: PlanData);
|
|
46
51
|
/** The account the organization records a calculated cost type in. */
|
|
47
52
|
costAccount(type: CostType): Id | undefined;
|
|
48
|
-
warn(code: string, message: string, key?: string): void;
|
|
53
|
+
warn(code: string, message: string, key?: string, scope?: WarningScope): void;
|
|
49
54
|
book(entry: EntryInput): void;
|
|
50
55
|
/**
|
|
51
56
|
* An entry's function: the line's if set. Otherwise a cost charged to a program is `program`
|
package/dist/planning/context.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { fiscalYearDates, within } from "./dates.js";
|
|
2
|
+
/** The warnings about what the blocks are given, or when they run. The rest are about the data. */
|
|
3
|
+
const ALLOCATION_WARNINGS = new Set([
|
|
4
|
+
"over_inventory_boats",
|
|
5
|
+
"over_inventory_resource",
|
|
6
|
+
"over_capacity",
|
|
7
|
+
"unallocated_activity",
|
|
8
|
+
"closed_date",
|
|
9
|
+
"position_double_booked",
|
|
10
|
+
]);
|
|
2
11
|
/** Lookups over the input, and the entries recorded so far, shared by every step. */
|
|
3
12
|
export class Context {
|
|
4
13
|
/** The plan year's first and last days. */
|
|
@@ -47,10 +56,19 @@ export class Context {
|
|
|
47
56
|
}
|
|
48
57
|
return account;
|
|
49
58
|
}
|
|
50
|
-
warn(code, message, key) {
|
|
51
|
-
|
|
59
|
+
warn(code, message, key, scope = {}) {
|
|
60
|
+
const same = (w) => w.code === code && w.message === message && w.version_id === scope.version_id;
|
|
61
|
+
if (this.warnings.some(same))
|
|
52
62
|
return;
|
|
53
|
-
|
|
63
|
+
const category = ALLOCATION_WARNINGS.has(code) ? "allocation" : "data";
|
|
64
|
+
this.warnings.push({
|
|
65
|
+
code,
|
|
66
|
+
message,
|
|
67
|
+
category,
|
|
68
|
+
...(key !== undefined && { key }),
|
|
69
|
+
...(scope.version_id !== undefined && { version_id: scope.version_id }),
|
|
70
|
+
...(scope.date !== undefined && { date: scope.date }),
|
|
71
|
+
});
|
|
54
72
|
}
|
|
55
73
|
book(entry) {
|
|
56
74
|
if (entry.amount === 0 || !Number.isFinite(entry.amount))
|
package/dist/planning/engine.js
CHANGED
|
@@ -77,7 +77,7 @@ function summarizePositions(ctx, coaching, charged, bonuses, doubleBooked) {
|
|
|
77
77
|
];
|
|
78
78
|
for (const key of new Set(named)) {
|
|
79
79
|
if (!known.has(key)) {
|
|
80
|
-
ctx.warn("unknown_position", `A block
|
|
80
|
+
ctx.warn("unknown_position", `A block or bonus names missing salaried position ${key}`, key);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
return ctx.data.plan_salaried_positions.map((position) => {
|
|
@@ -11,61 +11,61 @@ export interface SalariedCoaching {
|
|
|
11
11
|
/** The wages for `hourly` hours. */
|
|
12
12
|
wages: number;
|
|
13
13
|
}
|
|
14
|
-
/** A salaried position's year of coaching two
|
|
14
|
+
/** A salaried position's year of coaching two blocks at once. */
|
|
15
15
|
export interface DoubleBooked {
|
|
16
|
-
/** Hours another coach would need: n − 1 for each hour it's on n
|
|
16
|
+
/** Hours another coach would need: n − 1 for each hour it's on n blocks at once. */
|
|
17
17
|
hours: number;
|
|
18
|
-
/** Dates it's on two
|
|
18
|
+
/** Dates it's on two blocks at once. */
|
|
19
19
|
dates: number;
|
|
20
20
|
}
|
|
21
21
|
/** Whole kids: `target_enrollment` if set, otherwise `capacity × fill_rate` rounded. */
|
|
22
22
|
export declare function enrollmentOf(offering: PlanOffering): number;
|
|
23
23
|
export declare class Staffing {
|
|
24
24
|
private readonly ctx;
|
|
25
|
-
private readonly boatClasses;
|
|
26
|
-
private readonly offeringBoatClasses;
|
|
27
25
|
private readonly covers;
|
|
28
26
|
private readonly warned;
|
|
29
27
|
constructor(ctx: Context);
|
|
28
|
+
/** Warns once for each row and plan copy, about the first date it's seen on. */
|
|
30
29
|
private warnOnce;
|
|
31
30
|
/** Whether holding `held` qualifies for `required`, through `qualification_covers`. */
|
|
32
31
|
qualifies(held: Id[], required: Id): boolean;
|
|
33
|
-
private levelFor;
|
|
34
|
-
private sailorsByClass;
|
|
35
|
-
private boatsFor;
|
|
36
|
-
private initial;
|
|
37
|
-
private blockFor;
|
|
38
32
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
33
|
+
* Gathers each block's activities, times and allocations. A block's paid time runs from its first
|
|
34
|
+
* activity's start to its last one's end, plus the longest setup and put-away. Its time on the
|
|
35
|
+
* water is the longest `on_water_minutes` its activities set, or else the span of those that sail.
|
|
41
36
|
*/
|
|
42
|
-
private
|
|
43
|
-
/**
|
|
44
|
-
private
|
|
37
|
+
private blocks;
|
|
38
|
+
/** Warns about each activity, other than travel, that's in no block, or in one with no staff. */
|
|
39
|
+
private checkUnallocated;
|
|
40
|
+
/** Each program's settings, by its product. */
|
|
41
|
+
private programs;
|
|
42
|
+
/** Whether a staff member holding `held`, at level `levelKey` if any, may coach a program coached at `required`. */
|
|
43
|
+
private qualifiedFor;
|
|
45
44
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* Warns when a block's sailors are more than its staff or its boats can safely take. Each program's
|
|
46
|
+
* sailors count against its own sailors per coach, and a coach counts if they're at the staff
|
|
47
|
+
* level of one of the block's programs, or qualified for it. Boats count the ones sailors bring.
|
|
48
48
|
*/
|
|
49
|
-
private
|
|
49
|
+
private checkCapacity;
|
|
50
50
|
/**
|
|
51
|
-
* Warns when
|
|
52
|
-
*
|
|
51
|
+
* Warns when the organization's boats of one class, or a resource such as RIBs, are given to more
|
|
52
|
+
* blocks at once on a date than it has. Boats sailors bring don't count.
|
|
53
53
|
*/
|
|
54
|
-
private
|
|
54
|
+
private checkInventory;
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
56
|
+
* Warns when a salaried position coaches two blocks whose times overlap. It's still charged to
|
|
57
|
+
* both. Returns each position's double-booked hours and dates.
|
|
58
58
|
*/
|
|
59
|
-
private
|
|
60
|
-
/**
|
|
59
|
+
private checkDoubleBooked;
|
|
60
|
+
/** Costs every block's staff, RIBs, boats and resources, and checks them against the limits. */
|
|
61
61
|
run(scheduled: ScheduledActivity[]): {
|
|
62
62
|
activities: ComputedActivity[];
|
|
63
63
|
coaching: SalariedCoaching[];
|
|
64
64
|
doubleBooked: Map<Id, DoubleBooked>;
|
|
65
65
|
};
|
|
66
66
|
/**
|
|
67
|
-
* A trip's coaches each fly and stay; the miles are driven once. Its coaches are
|
|
68
|
-
*
|
|
67
|
+
* A trip's coaches each fly and stay; the miles are driven once. Its coaches are the staff on the
|
|
68
|
+
* block of its busiest travel day.
|
|
69
69
|
*/
|
|
70
70
|
private bookTrips;
|
|
71
71
|
}
|